Search code examples
node.jsreactjsnext.jsrails-api

How can I store user information in Next.JS + Rails API backend?


I'm new to Next.js and React in general, I used to develop the whole application with Rails.

I want to combine rails API with Next.js. My JWT backend has an endpoint that returns a JSON object containing user information when requested with JWT token in header. In my _app.js, I'm trying to authenticate users using this backend by using useState and useEffect like this:

export default function MyApp(props) {
  const [user, setUser] = useState({})

  useEffect(function () {
    const token = localStorage.getItem('token')
    if (token) {
      fetch('http://localhost:3001/auto_login', {
        headers: {
          Authorization: `Bearer ${token}`,
        },
      })
        .then((resp) => resp.json())
        .then((data) => {
          console.log(data) // {id: 1, email: "test@example.com"}
          setUser(data)
          console.log(user) // {}
        })
    }
  }, [])

  return (
    <>
      { // some code }
    </>
  )
}

In my first console.log, it returns an object that I would like to store in user. However, in my second console.log where I expect to return the same result, I get an empty object instead.

Am I missing anything, or are there any points that I have to take into consideration as a whole? I've already tried implementing this with async/await, but that didn't seem to solve my problem.


Solution

  • It's because that effect isn't aware of the dependent objects state change.

    If you did this (see code below) you'd see user being logged. Eg within the context of the 1st effect's 1st run, even though you're setting the user state, inside the effect it's not aware of the new value.

    The sequence is like this.

    1. Component loads
    2. Effect runs with initial state of [] (this effectively means run once, and uses the current state, user => {})
    3. state [] => console.log(data)
    4. state [] => setUser(data)
    5. state [] => console.log(user) // currently {}
    6. effect is done.

    Look at the useEffect explanation here

    export default function MyApp(props) {
     const [user, setUser] = useState({ email: null, id: null })
    
     // same useEffect / modify your existing code 
     // you could add user in here .but then the api call will fire again, 
     // thus an infinite loop could happen, so you would need to wrap 
     // the call in an if to check to prevent that, see my alterations
     useEffect(function () {
        const token = localStorage.getItem('token')
        if (token && !user.id) {
          fetch('http://localhost:3001/auto_login', {
            headers: {
              Authorization: `Bearer ${token}`,
            },
          })
            .then((resp) => resp.json())
            .then((data) => {
              console.log(data) // {id: 1, email: "test@example.com"}
              setUser(data)
              console.log(user);
            })
        }
      }, [user]); // [] => changed to => [user]
    
    
     // new / additional useEffect 
     // alternatively this effect will trigger when 
     // this objects state changes (and you wont need the checks as per above)
      useEffect(() => {
        console.log(user);
      }, [user]);  
    
      return (
        <>
          { // some code, e.g if you referenced user here, example. user.email, it would work. }
          { // Would probably initially be empty, when when the ajax call completes the value would/could be set }
        </>
      )
    }