skip to Main Content

I am using Redux and when I send a get request to authenticate user it is working, creating a token in cookies, but in Redux the state is not changing. What am I doing wrong? When reloading the page my token and user data are valid, here are some code samples.

After successful login my state is:

{
  "user": {
    "isAuthenticated": false
  }
}

Yet is should be true

I logged in before useEffect and it says true, because I have token its correct, so how do I pass it inside useEffect or when page is opened? I want to toast when it is true.

User reducer:

import { createReducer } from "@reduxjs/toolkit"

const initialState = {
  isAuthenticated: false,
}

export const userReducer = createReducer(initialState, (builder) => {
  builder
    .addCase('AuthenticateUserRequest', (state) => {
      state.loading = true
    })
    .addCase('AuthenticateUserSuccess', (state, action) => {
      state.isAuthenticated = true
      state.user = action.payload
      state.loading = false
    })
    .addCase('AuthenticateUserFailure', (state, action) => {
      state.isAuthenticated = false
      state.error = action.payload
      state.loading = false
    })
    .addCase('ClearErrors', (state) => {
      state.error = null
    })
})

User action:

import axios from "axios"
import { authenticateUserUrl } from '../../constants'

export const authenticateUser = () => async (dispatch) => {
  try {
    dispatch({
      type: 'AuthenticateUserRequest'
    })

    const { data } = await axios.get(authenticateUserUrl, { withCredentials: true })

    dispatch({
      type: 'AuthenticateUserSuccess',
      payload: data.user,
    })
  } catch (err) {
    dispatch({
      type: 'AuthenticateUserFailure',
      payload: err.response.data.message
    })
  }
}

store:

import { configureStore } from "@reduxjs/toolkit"
import { userReducer } from "./reducers/user"

const Store = configureStore({
  reducer: {
    user: userReducer,
  },
})
  
export default Store

app:

import { useContext, useEffect } from "react"
import { authenticateUser } from "./redux/actions/user"

const App = () => {
  var store = useContext(ReactReduxContext).store.getState()

  useEffect(() => {
    Store.dispatch(authenticateUser)
    console.log(JSON.stringify(store))  
  })

3

Answers


  1. Chosen as BEST ANSWER

    Well turns out the solution is:

    const { isAuthenticated, user, loading, error } = useSelector((state) => state.user)
        
          useEffect(() => {
            Store.dispatch(authenticateUser()) // Dispatch the action once on component mount
          }, []) // Empty dependency array to ensure this runs only once
        
          useEffect(() => {
            if (isAuthenticated) {
              console.log("User State:", { isAuthenticated, user, loading, error })
            }
          }, [isAuthenticated, user, loading, error]) 
    

  2. Issue

    The issue here is that of a stale Javascript closure. Redux state updates are synchronous, but you are attempting to console log the store value from the render cycle prior to the effect running.

    const App = () => {
      var store = useContext(ReactReduxContext).store.getState(); // <-- accessed here
    
      useEffect(() => {
        Store.dispatch(authenticateUser) ;  // <-- updates state
        console.log(JSON.stringify(store)); // <-- still old value from outer closure
      })
    
      ...
    };
    

    Solution

    You can access the store using the useStore hook and then access the current state value in the effect callback.

    const App = () => {
      const store = useStore();
    
      useEffect(() => {
        store.dispatch(authenticateUser);
        console.log(JSON.stringify(store.getState()));
      }, []);
    
      ...
    };
    

    Using the useStore hook and dispatch and getState methods are not the normal way of accessing the store in a React component. It’s far more common to use the useDispatch and useSelector hooks to dispatch actions to the store and subscribe to changes from the store. Use a separate effect to log the user state when it updates.

    Example:

    const App = () => {
      const dispatch = useDispatch();
      const user = useSelector(state => state.user);
    
      useEffect(() => {
        dispatch(authenticateUser);
      }, [dispatch]);
    
      useEffect(() => {
        console.log(JSON.stringify(user));
      }, [user]);
    
      ...
    };
    
    Login or Signup to reply.
  3. Solution
    Use Use Selector to grab updated values

    Login or Signup to reply.
Please signup or login to give your own answer.
Back To Top
Search