Reputation: 8177
I'm trying to create a query page using redux, axios and an API. In useEffect()
, if the variable from the useSelector
is empty and the isLoading
state is false
, then I do the API request. However even without any changes to these variables (already in the dependency list), the useEffect
keeps firing the API request indefinitely).
And while inspecting the state, I see integrationStatusLoading
changing from true
to false
all the time. It seems that the useEffect
is fired several times even before having completed the previous run.
My reducer:
import * as spreadsheetActions from './spreadsheetActions';
export const INITIAL_STATE = {
messagesLog: [],
loading: false,
modalIsOpen: false,
integration: {
rows: [],
loading: false
}
};
export default (state = INITIAL_STATE, action) => {
switch (action.type) {
case spreadsheetActions.SET_INTEGRATION_STATUS:
return { ...state, integration: {
...state.integration,
rows: action.payload
}};
case spreadsheetActions.SET_LOADING_INTEGRATION_STATUS:
return { ...state, integration: {
...state.integration,
loading: action.payload
}};
default:
return state;
}
};
The jsx:
const IntegrationStatusContainer = function() {
const classes = useStyles();
const dispatch = useDispatch();
const integrationStatusData = useSelector(state => state.spreadsheet.integration.rows);
const integrationStatusLoading = useSelector(state => state.spreadsheet.integration.loading);
useEffect(() => {
if (isEmpty(integrationStatusData) && !integrationStatusLoading) {
dispatch(spreadsheetOperations.getIntegrationStatus());
}
}, [dispatch, integrationStatusData, integrationStatusLoading]);
return (
<IntegrationStatusTable items={integrationStatusData} isLoading={integrationStatusLoading} />
);
};
export default IntegrationStatusContainer;
spreadsheetOperations.js
export const getIntegrationStatus = () => async dispatch => {
dispatch(spreadsheetActions.setLoadingIntegrationStatus(true));
let response = await spreadsheetManager.getIntegrationStatus();
batch(() => {
dispatch(spreadsheetActions.setLoadingIntegrationStatus(false));
dispatch(spreadsheetActions.setIntegrationStatus(response));
});
};
spreadsheetManager.js
getIntegrationStatus = async () => {
const getIntegrationStatusResult = await spreadsheetService.getIntegrationStatus();
return getIntegrationStatusResult;
};
spreadsheetService.js
getIntegrationStatus = async () => {
const integrationStatusResponse = await axios.get(
`${BASE_URL}/api/integration_status`
);
return integrationStatusResponse.data;
};
}
spreadsheetActions.js
export const setIntegrationStatus = rows => ({
type: SET_INTEGRATION_STATUS,
payload: rows
});
export const setLoadingIntegrationStatus = status => ({
type: SET_LOADING_INTEGRATION_STATUS,
payload: status
});
What is wrong with my code? How can I stop the infinite loop?
Upvotes: 0
Views: 251
Reputation: 8177
Problem solved by creating a new fetchStatus
state with three possible values (pending, fulfilled and rejected), then in my if I've added a condition checking for fetchStatus === 'pending'
.
Upvotes: 1
Reputation: 616
What is the expected functionality of this component? That the API call only fires once?
Your store updates to the isLoading flag will cause a render every time it changes. At the moment your cycle looks like it would be:
Have you console logged the variables inside the effect as well as the redux state? If the Redux state is changing then it will fire a render on the component.
I think the simplest solution is to remove the isLoading flag from the dependancies, unless you have a retry scenario you're trying to achieve but I'd be looking at redux-saga for a solution to that.
Upvotes: 0
Reputation: 19
I see you are replying on isEmpty to check whether API should be called. Can you check if isEmpty is working as expected or the API is always returning empty response?
Upvotes: 0