Home > Mobile >  Trying to load content with useEffect and context
Trying to load content with useEffect and context

Time:02-13

I am trying to show information of a state into a component. I am using the context to load information from different origin.

 const router = useRouter()
  const [sidebarOpen, setSidebarOpen] = useState(false)
  const [loadUser, setLoadUser] = useState({})

  const { state, dispatch } = useContext(Context)
  const { user } = state
  
  useEffect(() => {  
    if (user == null) {
      router.push("/auth/login")
    }

    setLoadUser(user)
  }, [user])

That code is inside a dashboard component. The idea is to get the user information into the state to show it on the dashboard. The problem is that the useEffect is executed at the same time that the content is rendered, therefore it does not have time to load the information, and the variables are null for me.

Here is an image of how the loadState variable behaves once inside the render.

enter image description here

I am using nextjs by the way.

I am passing the context with a provider to the App. And wrapped it.

// initial state
const initialState = {
  user: null
};

// Create context
const Context = createContext()

// root reducer
const reducer = (state, action) => {
  const { type, payload } = action;

  switch (type) {
    case "LOGIN":
      return { ...state, user: payload};
    
    case "LOGOUT":
      return { ...state, user: null };

      default:
      return state;
  }
}

// context provider
const Provider = ({ children }) => {
  const [state, dispatch] = useReducer(reducer, initialState);
  const router = useRouter();

  useEffect(() => {
    dispatch({
      type: "LOGIN",
      payload: JSON.parse(window.localStorage.getItem("user"))
    })
  }, [])

  axios.interceptors.response.use(
    function (response) {
      return response
    },

    function (error) {
      let res = error.response;
      console.log(error)

      if (res.data.status === 401 && res.config && !res.config.__isRetryRequest) {
        return new Promise((resolve, reject) => {
          axios.get(`${process.env.NEXT_PUBLIC_API}/auth/logout`)
            .then(res => {
              dispatch({ type: "LOGOUT" })
              window.localStorage.removeItem("user")
              router.push('/auth/login')
            })
            .catch(error => {
              reject(error)
            })
        })
      }

      return Promise.reject(error);
    }
  )

  useEffect(() => {
    const getCsrfToken = async () => {
      const { data } = await axios.get(`${process.env.NEXT_PUBLIC_API}/csrf-token`)
      axios.defaults.headers["X-CSRF-Token"] = data.csrfToken
    }

    getCsrfToken()
  }, [])

  return (
    <Context.Provider value={{ state, dispatch }}>
      {children}
    </Context.Provider>
  )
}

export { Context, Provider }

CodePudding user response:

Add user as a dependency in useEffect. useEffect will be called whenever user or anything else in the array change.

  useEffect(() => {  
    if (user == null) {
      router.push("/auth/login")
    }

    setLoadUser(user)
  }, [user]) // add here

CodePudding user response:

Add dependecy array. That shoud be quick fix.

  useEffect(() => {  
    if (user == null) {
      router.push("/auth/login")
    }

    setLoadUser(user)
  }, [state]) // dependecy array looks at state changes

You can read more about dependecy array on the official react documentation: https://reactjs.org/docs/hooks-effect.html

proper fix:

whenever you are using api calls in your app, those are asyncrhonus. You should use some loading state.

for example

const Context = () => {
   const [loading, setLoading] = useState(true)
   const [user, setUser] = useState(true)
   
   fetch('api/user').than(r=> setLoading(false);setUser(r.user))
// rest of the context code
}

and in component itself:

  const  state = useContext(Context)


  useEffect(() => {  
     state.loading ? null : setLoadUser(user)
 
  }, [state]) // 
  • Related