Reputation: 1138
I am trying to develop a React application that shows a list of items using Hooks but an infinite loop is happening. I think the state is not being identified as updated but I cannot tell why.
I have seen at the documentation that it could be the dependencies that I should add on the useEffect function, but when I remove the dependencies on useEffect it solves the problem. Moreover this is not what the documentation recommends to do, they recommend to put all dependencies on the list.
The codesandbox is here: https://codesandbox.io/s/react-listing-forked-6sd99 and if I uncomment the line 30 at Dashboard.ts
it is possible to see the infinite loop, this is the line that calls the dispatch
function (and also codesandbox will freeze for some seconds).
The useSelector and useEffect functions are as below, located at the Dashboard.tsx
file:
const catalog = useSelector<RootState, MetricState>(
(state) => state.fetchMetrics
);
console.log(catalog);
const dispatch = useDispatch();
const classes = useStyles();
useEffect(() => {
// dispatch(appReducer.actionCreators.fetchMetrics());
},[catalog, dispatch]));
The mocked data is as below, on the apiCreators.js
file
export function fetchMetrics() {
const action = {
type: ACTION_TYPES.FETCH_METRICS,
payload: []
};
return fetchMetricsCall(action);
const fetchMetricsCall = (action) => async (dispatch) => {
try {
dispatch({
type: action.type,
payload: {
metrics: [
{
name: "one",
owner: {
team: "test"
}
},
{
name: "one",
owner: {
team: "test"
}
}
] //contains the data to be passed to reducer
}.metrics
});
} catch (e) {
dispatch({
type: ACTION_TYPES.FAILURE,
payload: console.log(e) //TODO: fix return type
});
}
};
Upvotes: 2
Views: 2330
Reputation: 622
The main issue is the catalog
inside [catalog, dispatch]
found here:
useEffect(() => {
// dispatch(appReducer.actionCreators.fetchMetrics());
},[catalog, dispatch]));
The issue is that dispatch(appReducer.actionCreators.fetchMetrics())
is causing state.fetchMetrics (aka catalog)
to update (which is an object), and javascript is "weird" when it comes to comparing objects/arrays.
So let's look at this:
let x = {a: 0}
let y = {a: 0}
let z = x
console.log(x === y) // this is FALSE
console.log(x === x) // this is TRUE
console.log(z === x) // this is TRUE
This article does a better job of going into it: http://adripofjavascript.com/blog/drips/object-equality-in-javascript.html
useEffect
is doing something like if(oldState.catalog !== newState.catalog) {...rerender component}
whenever there's a change in state (I feel like this might be really inaccurate, but I hope it helps kind of illustrate what's going on). So the component rerenders and makes a new call to dispatch
, which updates the state and since one of the dependancies inside useEffect
is an object, it's impossible to make an update that will tell useEffect
to "stop rerendering".
There are ways to work around this, but you shouldn't need the catalog
inside the [catalog, dispatch]
. Simply removing it seems to fix the issue?
Upvotes: 3