In general is it better to use one or many useEffect hooks in a single component?

ReactjsPerformanceReact HooksArchitectureUse Effect

Reactjs Problem Overview


I have some side effects to apply in my react component and want to know how to organize them:

  • as a single useEffect
  • or several useEffects

Which is better in terms of performance and architecture?

Reactjs Solutions


Solution 1 - Reactjs

The pattern that you need to follow depends on your use case.

First: You might have a situation where you need to add event listener during the initial mount and clean them up at unmount and another case where a particular listener needs to be cleaned up and re-added on a prop change.

In such a case, using two different useEffect is better to keep the relevant logic together as well as having performance benefits

useEffect(() => {
   // adding event listeners on mount here
   return () => {
       // cleaning up the listeners here
   }
}, []);

useEffect(() => {
   // adding listeners everytime props.x changes
   return () => {
       // removing the listener when props.x changes
   }
}, [props.x])

Second: You need to trigger an API call or some other side-effect when any of the state or props change from a defined set. In such a case a single useEffect with the relevant dependencies to monitor would be better

useEffect(() => {
    // side effect here on change of any of props.x or stateY
}, [props.x, stateY])

Third: You need separate side-effect for different sets of changes. In such a case, separate out relevant side-effects into different useEffects

useEffect(() => {
   // some side-effect on change of props.x
}, [props.x])

useEffect(() => {
   // another side-effect on change of stateX or stateY 
}, [stateX, stateY])

Solution 2 - Reactjs

You should use multiple effects to separate concerns as suggested by reactjs.org.

Solution 3 - Reactjs

It's perfectly fine to have have multiple useEffect.

Here's how one of my setups looks like:

/*
 * Backend tags list have changed add the changes if needed
 */
useEffect(() => {
	setTagsList(setTagsAdded);
}, [setTagsAdded]);

/*
 * Backend files have changed add the changes if needed
 */
useEffect(() => {
	for (let i = 0; i < changedFilesMeta.length; i += 1) {
		// Is the list item value changed
		if (changedFilesMeta[i].id === currentEditableFile.id) {
			unstable_batchedUpdates(() => {
				setTags(changedFilesMeta[i].tags ? changedFilesMeta[i].tags : []);
			});
		}
	}
}, [changedFilesMeta]);

/*
 * Reset when user select new files using the filepicker
 */
useEffect(() => {
	if (setNewFiles.length > 0) {
		unstable_batchedUpdates(() => {
			setCurrentFile(null);
			setDescription('');
			setTitle('');
			setTags([]);
		});
	}
}, [setNewFiles]);

/*
 * User selecet to edit a file, change to that file
 */
useEffect(() => {
	// When user select a file to edit it
	if (currentEditableFile && currentEditableFile !== theCurrentFile) {
		setCurrentFile(currentEditableFile);
		unstable_batchedUpdates(() => {
			setDescription(currentEditableFile.description);
			setTitle(currentEditableFile.title);
			setTags(currentEditableFile.tags);
		});
	}
}, [currentEditableFile]);

Attributions

All content for this solution is sourced from the original question on Stackoverflow.

The content on this page is licensed under the Attribution-ShareAlike 4.0 International (CC BY-SA 4.0) license.

Content TypeOriginal AuthorOriginal Content on Stackoverflow
QuestionVadimView Question on Stackoverflow
Solution 1 - ReactjsShubham KhatriView Answer on Stackoverflow
Solution 2 - ReactjsGuy EngelView Answer on Stackoverflow
Solution 3 - ReactjsErikView Answer on Stackoverflow