Reputation: 85
I've been going through my React/Redux application and fixing linting issue, and I've been struggling with handling useEffect/useCallback dependencies.
I've read through this guide and multiple other sources, all of which have emphasized the importance of respecting react-hooks/exhaustive-deps and making sure dependencies are always included in the array. However, I'm running into multiple situations where I don't know how to properly include all the dependencies without causing unnecessary side effects to trigger.
Here's the main example:
I'm building a chat interface and the ChatScroller component shows the user the current list of chat messages. The data fetching logic looks something like this:
const { chatID } = props;
const [chatMessages, setChatMessages] = useState([]);
const loadMessages = useCallback(() => {
const oldestMessage = chatMessages.length > 0 ? chatMessages[0].timestamp : undefined;
//Call api and tell it to get the 10 most recent chat messages for `chatID`, whose timestamp is before oldestMessage
}, [chatID]);
useEffect(() => {
loadMessages();
}, [loadMessages]);
What's happening is the loadMessages
function depends on the existing list of chatMessages in order to tell the api where to paginate in the chat history. However, if I include chatMessages
in the dependency array (or even just include the timestamp of the oldest message), then loadMessages
will reload immediately after it's called, causing the useEffect to trigger again.
I also run into a similar issue with a PostList component I'm building, which has a similar requirement to tell the api where to paginate. The general pattern I'm running into is a function requires some external data to do its job, however, I don't necessarily need that function to re-sync if the data ends up changing later. I'm running into this issue in many different components, which is making we wonder whether I'm thinking about useEffect wrong, or whether exceptions to exhaustive-deps are more common than I expected.
Upvotes: 0
Views: 392
Reputation: 413
You can use an extra state variable to decide when to make next API call. Something like below.
const { chatID } = props;
const [chatMessages, setChatMessages] = useState([]);
const [nextApiCall, setNextApiCall] = useState(false); // set to true based on the logic(scroll or whatever logic...)
useEffect(() => {
const loadMessages = () => {
const oldestMessage =
chatMessages.length > 0 ? chatMessages[0].timestamp : undefined;
// Call api and tell it to get the 10 most recent chat messages for `chatID`, whose timestamp is before oldestMessage
// setNextApiCall(false);
};
if (nextApiCall) {
loadMessages();
}
}, [chatMessages, nextApiCall]);
Upvotes: 1