简体   繁体   中英

React redux reducer as UseEffect dependency causes infinite loop

I am just diving deep into react. But the useEffect react hook still got me confused. I know that I can pass dependencies as an array to it to control rendering of the component. I have used props and local state to do and it works.

What's got me still confused is when I pass redux reducer as a dependency, it causes an infinite loop of rendering the component.

// users component

const usersComp = () => {
const users = useSelector(state => state.users); 

useEffect(
    // fetch users and update users state
    useDispatch().dispatch(getUsers)
,[users]) // <-- causes an infinite loop!! 

if(users.length){
    return( users.map(user => <p>{user}</p>))
}

}

// getUsers Redux Thunk function

export async function getUsers(dispatch, getState) {
fetch(endpoint)
    .then(response => response.json())
    .then(users => {
        dispatch({type: GET_USERS, payload: users})
    }).catch(err => console.error("Error: ", err));

}

// users reducer

  export default function usersReducer(state = [], action) {
     switch (action.type) {

        case GET_USERS : {
            return [...state, action.payload]
        }
    }
}

From what I understand users starts off as an empty array, then gets filled with data from an API call. So useEffect should fire twice; when the component has just mounted and then when users state changes from the API call. So what's causing the infinite loop?

Remove users from the useEffect dependency because you want to fetch users when component mounts not each time the users is changed.

useEffect(
    useDispatch().dispatch(getUsers)
,[]) // Now, it will fetch users ONLY ONCE when component is mounted

Explanation:

// Case 1
useEffect(() => {
  console.log("Mounted") // printed only once when component is mounted
}, [])

// Case 2
useEffect(() => {
  console.log("users changed") // printed each time when users is changed
}, [users])

So, if you do fetch in Case 2 , it will change users which will re-trigger the hook which will fetch the users again which changes the users and causes the hook to re-trigger ---> This is an infinite loop .

Update:

Why state.users is getting changed (in this code), as detected by useEffect , even when values of state.users are "SAME" (Same values)?

Whenever GET_USERS action is dispatched, reducer returns new state ( {...state, users: action.payload }) , it does so even when value of action.payload holds same value of users. This is why useEffect receives new users array. (They do shallow comparison).

Note that, [1, 2,3] is not equal to [1, 2,3] ie [1, 2,3] === [1, 2,3] returns false.

If for some reason, you want to return the same redux state, do return state in the reducer. This is often what we do in default case of switch of reducer.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM