I am refactoring some code and turning my class components into function components as a way of learning how to use Hooks and Effects. My code uses Redux for state management and axios for database requests with Thunk as middleware for handling asynchronicity. I'm having an issue in one component that does a get request to retrieve a list of customers on what used to be componentDidMount. No matter what I try, the useEffect function gets into an infinite loop and continues requesting the customer list.
The component in question, CustomersTable, gets a list of customers from the database and displays it in a table. The component is wrapped by a container component that uses Redux's connect to pass in the retrieved list of customers to the CustomersTable as a prop.
useEffect(() => {
loadCustomers(currentPage, itemsPerPage, sortProp, (ascending ? 'asc' : 'desc'), {});
}, []);
loadCustomers is a Redux action that uses axios to fetch the customer list. currentPage, itemsPerPage, sortProp and ascending are state variables that are initialized to specific values on 'component mount'
I would expect that because I use the empty array that this would run only once. Instead it runs continuously. I can't figure out why this is happening. My best guess is that when redux gets the list, it returns a new object for state and therefore the props change every time, which then triggers a re-render, which then fetches a new list. Am I using this wrong in that Redux isn't meant to be used with hooks like this?
I ended up getting this working by adding the following:
useEffect(() => {
if (!list.length) {
loadCustomers(currentPage, itemsPerPage, sortProp, (ascending ? 'asc' : 'desc'), {});
}
}, []);
I'm not sure this is the behavior I truly want though. If the list of customers was truly 0, then the code would continue to fetch the list. If the list were truly empty, then I would want it to fetch only once and then stop. Edit: Turns out this definitely doesn't work. It works for the initial load, but breaks the code for any delete or edit.
OK, providing more context here. The container component that wraps the CustomersTable is:
import { connect } from 'react-redux';
import loadCustomers from './actions/customersActions';
import { deleteCustomer } from './actions/customerActions';
import CustomersTable from './CustomersTableHooks';
function mapStateToProps(state) {
return {
customers: state.customers,
customer: state.customer
};
}
export default connect(mapStateToProps, { loadCustomers, deleteCustomer })(CustomersTable);
The action, loadCustomers is:
export default function loadCustomers(page = 1, itemsPerPage = 50, sortProp = 'id', sortOrder = 'asc', search = {}) {
return (dispatch) => {
dispatch(loadCustomersBegin());
return loadCustomersApi(page, itemsPerPage, sortProp, sortOrder, search)
.then(data => dispatch(loadCustomersSuccess(data)))
.catch(() => dispatch(loadCustomersFailure()));
};
}
the reducer for customers is:
export default function customersReducer(state = initialState, action) {
switch (action.type) {
case types.LOAD_CUSTOMERS_BEGIN:
return Object.assign({}, state, { isLoading: true, list: [], totalItems: 0 });
case types.LOAD_CUSTOMERS_SUCCESS:
return Object.assign({}, state, { isLoading: false, list: action.customers || [], totalItems: action.totalItems });
case types.LOAD_CUSTOMERS_FAILURE:
return Object.assign({}, state, { isLoading: false, list: [], totalItems: 0 });
default:
return state;
}
}
I unfortunately can't post much of the CustomersTable itself because things are named in a way that would tell you what company I'm working for.
So, if i understand your code correctly, you are dispatching the loadCustomers action in child component within useEffect but you read actual data in parents mapStateToProps.
That would, of course, create infinite loop as:
customers from store (or anything from the store, for that matter)customers in useEffectMoral of the story: don't dispatch from presentational components. or, in other words, dispatch an action from the same component you read those same properties from store.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With