As it's said, the useEffect hook is the place where we do the side-effects related part. I'm having a confusion of what dependencies should be passed in the dependency array of useEffect hook?
The React documentation says
If you use this optimization, make sure the array includes all values from the component scope (such as props and state) that change over time and that are used by the effect. Otherwise, your code will reference stale values from previous renders.
Consider an example:
export default function App() {
const [count, setCount] = React.useState(0);
React.useEffect(() => {
console.log("component mounted");
}, []);
const update = () => {
for(let i=0; i<5;i++){
setCount(count+i)
}
};
return (
<div>
{console.log(count)}
{count}
<button type="button" onClick={update}>
Add
</button>
</div>
);
}
Going with the above statement, we should pass the count variable as a dependency to useEffect, it will make the useEffect re run.
But in the above example, not passing the count dependency doesn't create any problem with the UI. There is not stale value. The variable updates as expected, UI re-renders with exact value, logs the current value.
So my question is, why should we pass the count variable as dependency to useEffect. The UI is working correctly?
UPDATE
I know the useEffect callback is triggered everytime when the value in dependency array changes. My question is what should go in to the dependency array? Do we really need to pass state variables?
Thanks in advance.
To demonstrate and understand the issue print the count variable inside
React.useEffect(() => {
console.log("component updated ", count);
}, []); // try to add it here
click again the button and see how it logs to the console
EDIT:
If you want to get notified by your IDE that you are missing dependencies then use this plugin
https://reactjs.org/docs/hooks-rules.html#eslint-plugin
Keep in mind that it will still complain if you want to simulate onMount
What should go into the dependency array?
Those things (props/state) that change over time and that are used by the effect.
In the example, the UI works correctly because setState re-renders the component.
But if we do some side-effect like calling an alert on change of count, we have to pass count to the dependency array. This will make sure the callback is called everytime the dependency (count) in our case, changes.
React.useEffect(() => {
alert(`Count ${count}`); // call everytime count changes
}, [count]); // makes the callback run everytime, the count updates.
If property from second argument in useEffect change - then component will rerender.
If you pass the count -> then component rerender after count change - one time.
React.useEffect(() => {
console.log("component mounted");
}, [count]);
Quick answer:
Pass everytime you need to trigger refreshing component.
useEffect will get called in an infinite loop unless you give it dependencies.
React.useEffect(() => {
console.log("Looped endlessly");
}); // dependencies parameter missing
Adding an empty dependency list will cause it to get called just once on component did mount
React.useEffect(() => {
console.log("Called once on component mount");
}, []); // empty dependency list
Add a state to the dependency list to get called when state gets updated
React.useEffect(() => {
console.log("Called once on component mount and whenever count changes");
console.log("Count: " + count);
}, [count]); // count as a dependency
Related
I have to fetch an API when a state changes, i've used useEffect adding the dependency like this
const [ activeLottery, setActiveLottery ] = useState();
useEffect(() => {
console.log('fetching')
}, [activeLottery])
return (
<div className="container">
<Carousel
lotteries={ lotteries }
isLoading={ isLoading }
slideClick={ setActiveLottery }
/>
</div>
);
The Component Carousel has an onClick function which changes activeLottery and it works properly since the state is mutated. But it executes the fetch when the component is rendered. Shouldn't do it when activeLottery changes?
Any hint would be much appreciated.
thanks
By default, useEffect always runs after the first render.
The dependency array in useEffect lets you specify the conditions to trigger it. If you provide useEffect an empty dependency array, it’ll run exactly once.
If you want to make API call when activeLottery changes then write condition inside useEffect.
useEffect(() => {
if(activeLottery?.length){ // if activeLottery is array then you can check it's length
// Your API call goes here
}
console.log('fetching')
}, [activeLottery])
Additionally, you can check out this awesome StackOverflow answer: https://stackoverflow.com/a/59841947/16420018
And this article by Dan Abramov: https://overreacted.io/a-complete-guide-to-useeffect/
I have a function below which i used as an array dependency to a useEffect handler
const handleInputUpdate = (event) => {
const eventValue = event.target.value;
setState({ ...state, answer_text: eventValue, trigger: true })
// console.log("I am changing for no reason")
}
Below is the useEffect handler
useEffect(() => console.log(" I am changing for no reason in useeffect"), [handleInputUpdate])
What i want is the useEffect handler to run only when the handleInputUpdate function is called but it runs also on component mount.
Here's what i've observed
The handleInputUpdate function doesn't run on component mount but only i need it to
Without respect to the above observation, the useEffect handler runs anyway.
Here's what i've tried
I tried consoling a text inside the handleInputUpdate function to see whether it runs on component render but it doesn't.
Even though the function doesn't run, the useEffect handler triggers anyway which is not what i want.
How can i solve this ?
Thanks in advance
useEffect dependency array is not used to trigger the effect when a function is called; the elements of the array are observed for any change and then trigger the effect.
In this case, handleInputUpdate will change on every render because it is not memoised, so the effect will also run on every render.
Since handleInputUpdate changes the state when it is called, you are better off adding that state to your useEffect dependency array:
useEffect(() => {
if (answer_text && trigger) {
console.log("I am changing for a reason in useeffect")
}
}, [answer_text, trigger])
The handleInputUpdate function, while it doesn't run on render, looks like it's created when the component runs, just before rendering. Since it won't be === to the value last in the dependency array - the handleInputUpdate from the prior render - the effect callback will run.
You need to observe changes to the answer_text value in state instead.
useEffect(() => {
// ...
}, [state.answer_text]);
I would also recommend separating out your state into different variables - these aren't class components, don't feel like you have to mash everything together into a single object structure.
const [text, setText] = useState('');
What's the different between useEffect when you pass it dependencies as the second parameter and useCallback?
Don't both essentially run the function/code passed as the first parameter whenever the dependencies passed as the second parameter change?
From what I've read the two hooks are intended to serve different purposes, but my question is whether they in actuality could be used interchangeably because they functionally do the same thing
They're too different.
useEffect will run the function inside when the dependency array changes.
useCallback will create a new function when the dependency array changes.
You can't switch useEffect with useCallback alone because you also need the logic to run the newly created function. (I suppose you could implement this if you used a ref as well, but that'd be quite strange.)
You can't switch useCallback with useEffect because you very often don't want to run the newly created function immediately - rather, you usually want to pass it as a prop to some other component.
useCallback primarily exists for optimization purposes, to reduce re-renders of a child component.
No, They are not same.
useEffect - is used to run side effects in the component when something changes. useEffect does
not return you anything. It just runs a piece of code in the component.
useCallback - Whereas useCallback returns a function, it does not execute the code actually. It is important to understand that
functions are objects in Javascript. If you don't use useCallback, the function you define inside the component is
re-created whenever the component rebuilds.
Example
Consider this example, this component will go in a infinite loop. Think Why?
const TestComponent = props => {
const testFunction = () => {
// does something.
};
useEffect(() => {
testFunction();
// The effect calls testFunction, hence it should declare it as a dependency
// Otherwise, if something about testFunction changes (e.g. the data it uses), the effect would run the outdated version of testFunction
}, [testFunction]);
};
Because on each render the testFunction
would be re-created and we already know that ueEffect will run the code when ever the testFunction changes. And since testFunction changes on each render, the useEffect will keep on running, and hence an infinite loop.
To fix this, we have to tell react, hey please don't re-create the testFunction on each render, create it only on first render (or when something changes on which it depends).
const TestComponent = props => {
const testFunction = useCallback(() => {
// does something.
}, []);
useEffect(() => {
testFunction();
// The effect calls testFunction, hence it should declare it as a dependency
// Otherwise, if something about testFunction changes (e.g. the data it uses), the effect would run the outdated version of testFunction
}, [testFunction]);
};
This won't be a infinite loop, since instance of testFunction will change only on first render and hence useEffect will run only once.
useEffect will run the function inside when the dependency array changes.
useCallback will create a new function when the dependency array changes.
Let's take an example, If I run the below code and click the first button it'll always rerender MemoComponent as well. Why because every time
we are passing new onClick function to this. To avoid re-rendering of MemoComponent what we can do is wrap onClick to useCallback. Whenever you want to create a new function pass state to the dependence array.
If you want to perform some action on state change you can write inside useEffect.
const Button = ({ onClick }) => {
console.log("Render");
return <button onClick={onClick}>Click</button>;
};
const MemoComponent = React.memo(Button);
export default function Home() {
const [state, setState] = useState(1);
useEffect(() => {
console.log(state); // this will execute when state changes
}, [state]);
const onClick = () => {};
// const onClick = useCallback(() => {},[])
return (
<main>
<button onClick={() => setState(1 + state)}>{state}</button>
<MemoComponent onClick={onClick} />
</main>
);
}
useEffect
It's the alternative for the class component lifecycle methods componentDidMount, componentWillUnmount, componentDidUpdate, etc. You can also use it to create a side effect when dependencies change, i.e. "If some variable changes, do this".
Whenever you have some logic that is executed as reaction to a state change or before a change is about to happen.
useEffect(() => {
// execute when state changed
() => {
// execute before state is changed
}
}, [state]);
OR
useEffect(() => {
// execute when state changed
() => {
// execute before state is changed
}
}, []);
useCallback
On every render, everything that's inside a functional component will run again. If a child component has a dependency on a function from the parent component, the child will re-render every time the parent re-renders even if that function "doesn't change" (the reference changes, but what the function does won't).
It's used for optimization by avoiding unnecessary renders from the child, making the function change the reference only when dependencies change. You should use it when a function is a dependency of a side effect e.g. useEffect.
Whenever you have a function that is depending on certain states. This hook is for performance optimization and prevents a function inside your component to be reassigned unless the depending state is changed.
const myFunction = useCallback(() => {
// execute your logic for myFunction
}, [state]);
Without useCallback, myFunction will be reassigned on every render. Therefore it uses more compute time as it would with useCallback.
Am using useEffect in a react functional component to fetch data from an external API but it keeps calling the API endpoint on render on the page .
Am looking for a way to stop the useeffect from running on render on the component
Use the dependency array (second argument to the useEffect), so you can specify when you need to run the useEffect.
The problem here is that you have not used the dependency array, so that it executes every time. By adding a dependency array, you specify the changes where you want useEffect to run.
useEffect(()=>{
},[<dependency array: which contains the properties>]);
If you leave the dependency array empty, it will run only once. Therefore if you want the API call to run only once, add an empty array as the second argument to your useEffect. This is your solution.
Like this:
useEffect(()=>{
//Your API Call
},[]);
useEffect is always meant to run after all the changes or render effects are update in the DOM. It will not run while or before the DOM is updated. You may not have given the second argument to useEffect, which if u do not provide will cause the useEffect to execute on each and every change. Assuming you only want to call the API just once when on after the first render you should provide an empty array.
Runs on all updates, see no second argument to useEffect:
useEffect(() => { /* call API */ });
Runs when the prop or state changes, see the second argument:
useEffect(() => { /* call API */ }, [prop, state]);
Runs only once, see the empty second argument:
useEffect(() => { /* call API */ }, []);
I recommend you to read the full documentation about the React useEffect hook.
Here is a easy example of using useEffect
function functionalComponent() {
const [data, setData] = React.useState(null);
React.useEffect(() => {
const url = 'https://randomuser.me/api/?results=10';
fetch(url)
.then(data => {
setData(data);
})
.catch(error => console.error(error))
}, []); // it's necessary to use [] to avoid the re-rendering
return <React.Fragment>
{data !== null && (
<React.Fragment>
{data.results.map(data => (
<div>
{data.gender}
</div>
))}
</React.Fragment>
)}
</React.Fragment>;
}
Maybe in your useEffect implementation you are avoiding the [] dependencies, this is a bit hard to understand if you come from class states. This on hooks review when a state element inside the hook change, for example if you are using an element that always change like a prop that you pass throught another component you might be setting inside the dependencies or another state, if you do not need any dependency just use it empty like the example above. As you can see in the documentation sometimes the dependencies are not used, this might generate an infinite loop.
So, I'm using hooks to manage the state of a set of forms, set up like so:
const [fieldValues, setFieldValues] = useState({}) // Nothing, at first
When setting the value, the state doesn't update:
const handleSetValues = values => {
const _fieldValues = {
...fieldValues,
...values
}
setFieldValues(_fieldValues)
console.log(fieldValues) // these won't be updated
setTimeout(() => {
console.log(fieldValues) // after ten seconds, it's still not updated
},10000)
}
If I call the function a second time, it'll have updated, but that's not gonna work for me.
I never saw behaviour like this in class components.
Is it meant to... like, not update? Or just update whenever it feels like it? Really confusing behaviour.
setFieldValues(_fieldValues) is an async call, means you won't able to get the result in the very next line of this.
You can use useEffect hook.
useEffect(() => {
// do your work here
}, [fieldValues]);
It seems from your question that you have background of Class components of React, so useEffect is similar to componentDidMount and componentDidUpdate lifecycle methods.
useEffect calls whenever the state in the dependency array (in your case [fieldValues]) changes and you get the updated value in useEffect body.
You can also perform componentWillUnmount work in useEffect as well.
Have a brief guide.
setFieldValues is an asynchronous function, so logging the value below the statement will not have any effect.
Regarding using setTimeout, the function would capture the current value of props being passed to it and hence that would be the value printed to the console. This is true to any JS function, see the snippet below:
function init(val) {
setTimeout(() => {
console.log(val);
}, 1000);
}
let counterVal = 1;
init(counterVal);
counterVal++;
So how can we print the values when the value changes? The easy mechanism is to use a useEffect:
useEffect(() => {
console.log(fieldValues)
}, [fieldValues]);