If you only want to run the function given to useEffect after the initial render, you can give it an empty array as second argument.
function MyComponent() {
useEffect(() => {
loadDataOnlyOnce();
}, []);
return <div> {/* ... */} </div>;
}
Answer from Tholle on Stack OverflowIf you only want to run the function given to useEffect after the initial render, you can give it an empty array as second argument.
function MyComponent() {
useEffect(() => {
loadDataOnlyOnce();
}, []);
return <div> {/* ... */} </div>;
}
TL;DR
useEffect(yourCallback, []) - will trigger the callback only after the first render.
Detailed explanation
useEffect runs by default after every render of the component (thus causing an effect).
When placing useEffect in your component you tell React you want to run the callback as an effect. React will run the effect after rendering and after performing the DOM updates.
If you pass only a callback - the callback will run after each render.
If passing a second argument (array), React will run the callback after the first render and every time one of the elements in the array is changed. for example when placing useEffect(() => console.log('hello'), [someVar, someOtherVar]) - the callback will run after the first render and after any render that one of someVar or someOtherVar are changed.
By passing the second argument an empty array, React will compare after each render the array and will see nothing was changed, thus calling the callback only after the first render.
Videos
How does the dependency array affect the useEffect hook's execution?
How can you skip the useEffect execution on the first render?
What is the purpose of passing an empty array to useEffect?
Hello all, i'm a fairly noob developper and new to react.
Is it possible to have a useEffect to only run when the dependency array changes and not at mount ?
Also i can't explain why but it seems to me a bad use of useEffect, is there a better way to achieve this ? Thanks
Lets take an example - I want to initialize the SDK of a partner who will be loading an iframe on my page
-
I can use a `useEffect` with empty dependency array. It will work fine in production but will be fired twice in development mode. And that does not feel right
-
I can use `useRef` and base my logic around it, But this does not look intuitive either.
What would be the best way to do this?
const TestComponent = () => {
const [isLoggedIn, setIsLoggedIn] = useState(false);
useEffect(() => {
console.log("Only run if isLoggedIn changes");
}, [isLoggedIn]);
return (
<div>
<h1>Test Component</h1>
</div>
);};
export default TestComponent;
surely it's only meant to execute when isLoggedIn changes?
You can use more than one useEffect().
For example, if my variable is data1, I can use all of this in my component:
useEffect( () => console.log("mount"), [] );
useEffect( () => console.log("data1 update"), [ data1 ] );
useEffect( () => console.log("any update") );
useEffect( () => () => console.log("data1 update or unmount"), [ data1 ] );
useEffect( () => () => console.log("unmount"), [] );
Since the cleanup is not dependent on the username, you could put the cleanup in a separate useEffect that is given an empty array as second argument.
Example
const { useState, useEffect } = React;
const ForExample = () => {
const [name, setName] = useState("");
const [username, setUsername] = useState("");
useEffect(
() => {
console.log("effect");
},
[username]
);
useEffect(() => {
return () => {
console.log("cleaned up");
};
}, []);
const handleName = e => {
const { value } = e.target;
setName(value);
};
const handleUsername = e => {
const { value } = e.target;
setUsername(value);
};
return (
<div>
<div>
<input value={name} onChange={handleName} />
<input value={username} onChange={handleUsername} />
</div>
<div>
<div>
<span>{name}</span>
</div>
<div>
<span>{username}</span>
</div>
</div>
</div>
);
};
function App() {
const [shouldRender, setShouldRender] = useState(true);
useEffect(() => {
setTimeout(() => {
setShouldRender(false);
}, 5000);
}, []);
return shouldRender ? <ForExample /> : null;
}
ReactDOM.render(<App />, document.getElementById("root"));
<script src="https://unpkg.com/react@16/umd/react.development.js"></script>
<script src="https://unpkg.com/react-dom@16/umd/react-dom.development.js"></script>
<div id="root"></div>
There are a couple of things there. First, to fix the code, you could update your useEffect to this:
useEffect(() => {
messagesRef.on('child added', snapshot => {
const message = snapshot.val();
message.key = snapshot.key;
setMessages(messages.concat(message)); // See Note 1
}, []); // See Note 2
Note 1
The setMessages line is how you update your state. useState is a little bit different from the "old" setState in a sense that will completely replace the state value. React documentation says:
This is because when we update a state variable, we replace its value. This is different from this.setState in a class, which merges the updated fields into the object.
Note 2
React Hooks changes the way we build apps and it is not a real "translation" from the old lifecycles.
The empty brackets ([]) in the last line, will make your code "similar" to componentDidMount, but most importantly, will make your effect run only once.
Dan Abramov said (removed some of the original text):
While you can useEffect(fn, []), it’s not an exact equivalent. Unlike componentDidMount, it will capture props and state. So even inside the callbacks, you’ll see the initial props and state. (...) Keep in mind that the mental model for effects is different from componentDidMount and other lifecycles, and trying to find their exact equivalents may confuse you more than help. To get productive, you need to “think in effects”, and their mental model is closer to implementing synchronization than to responding to lifecycle events.
Full article about useEffect here.
You tried to declare the state again instead of using the state updater
useEffect(() => {
messagesRef.on('child added', snapshot => {
const message = snapshot.val();
message.key = snapshot.key;
// setMessages is the state updater for messages
// instead of an object with messages: messagesArray
// just save it as an array the name is already messages
setMessages([...messages, message]);
});
// useEffect takes an array as second argument with the dependencies
// of the effect, if one of the dependencies changes the effect will rerun
// provide an empty array if you want to run this effect only on mount
}, []);