Make React useEffect hook not run on initial render
We can use the useRef
hook to store any mutable value we like, so we could use that to keep track of if it's the first time the useEffect
function is being run.
If we want the effect to run in the same phase that componentDidUpdate
does, we can use useLayoutEffect
instead.
Example
const { useState, useRef, useLayoutEffect } = React;
function ComponentDidUpdateFunction() { const [count, setCount] = useState(0);
const firstUpdate = useRef(true); useLayoutEffect(() => { if (firstUpdate.current) { firstUpdate.current = false; return; }
console.log("componentDidUpdateFunction"); });
return ( <div> <p>componentDidUpdateFunction: {count} times</p> <button onClick={() => { setCount(count + 1); }} > Click Me </button> </div> );}
ReactDOM.render( <ComponentDidUpdateFunction />, document.getElementById("app"));
<script src="https://unpkg.com/react@16.7.0-alpha.0/umd/react.development.js"></script><script src="https://unpkg.com/react-dom@16.7.0-alpha.0/umd/react-dom.development.js"></script>
<div id="app"></div>
useEffect does not run in initial render
The useEffect hook actually renders on initial component mount but the problem here is that the way you generate piano node .
You need to somehow put the value in a state and update state so the component re-renders everytime the state changes and also when the component mounts .
const [value , setValue ] = React.useState("1")
React.useEffect(() => {
// your logic
// setValue("2")
}, [value]);
Now the useEffect hook will run on component mount and then everytime the state value changes .
Note
If the value change simultaneously it will cause rendering problem in the component so a better option in this case is assigning an event listener in useEffect like this :
const handleScroll = () => {
// your logic
}
React.useEffect(() => {
if (typeof window !== "undefined"){
window.addEventListener("scroll" , handleScroll)
}
return () => {
// on component unmount remove event listener
if (typeof window !== "undefined"){
window.removeEventListener("scroll" , handleScroll)
}
}
}, []);
With useEffect, how can I skip applying an effect upon the initial render?
As the guide states,
The Effect Hook, useEffect, adds the ability to perform side effects from a function component. It serves the same purpose as componentDidMount, componentDidUpdate, and componentWillUnmount in React classes, but unified into a single API.
In this example from the guide it's expected that count
is 0 only on initial render:
const [count, setCount] = useState(0);
So it will work as componentDidUpdate
with additional check:
useEffect(() => {
if (count)
document.title = `You clicked ${count} times`;
}, [count]);
This is basically how custom hook that can be used instead of useEffect
may work:
function useDidUpdateEffect(fn, inputs) {
const didMountRef = useRef(false);
useEffect(() => {
if (didMountRef.current) {
return fn();
}
didMountRef.current = true;
}, inputs);
}
Credits go to @Tholle for suggesting useRef
instead of setState
.
UseEffect firing on initial render
You can wait until the key
& device
props are available:
useEffect(() => {
key && device && fetch(`APILINK&key=${key}&id=${device}`)
.then((res) => res.json())
.then(setData)
}, [device, key])
useEffect
is called right after the first render, regardless of the dependency array, so this protection above will not run the fetch
call until those variables are available (when the dependency array "sees" there was a change and calls the useEffect
callback).
You might want to show a loader if the key
prop might take a while to be available.
I suggest to de-couple the logic which gets the data from the useEffect
, because you might want to call getData
directly, and it's also better for testing and general code order.
const getData = (key, device) =>
key && device && fetch(`APILINK&key=${key}&id=${device}`)
.then((res) => res.json())
.then(setData);
useEffect(() => {
getData(key, device)
}, [device, key])
Also, there's no need to fornull
in useState(null)
, because useState()
will behave identical.
Why does useEffect hook with its dependencies run after first component render
I have prevent mounting using this:
let storageRef = useRef(true);
useEffect(() => {
if (!storageRef.current) {
localStorage.setItem("cart", JSON.stringify(cart));
}
return () => { storageRef.current = false; }
}, [cart])
Is it a good idea?
Why is my useEffect not running on first render?
below code will not work as map need return statement, so it would be undefined, useEffect will console the empty array as define in the state. also you should not use let in render components as it will always initate, use may be useRef for that. also if your component consist js code, better to make it helper file and use it, would save and boost the performance.
words.map(word =>{
if(word.length <= 2){
scrambledWord.push(word);
}
else{
scrambledWord.push(scrambleWord(word));
}
})
How to trigger an useEffect hook in React?
You can add props.id
to dependency array so it would rerun on id change. Having an empty dependency array makes it run only once.
useEffect(() => {
async function fetchOne() {
const response = await productService.getOne(props.id)
setProduct(response.data)
}
fetchOne()
}, [props.id])
Related Topics
Html5 Drag and Drop Anywhere on the Screen
How to Disable a Href Link in JavaScript
Enter Key Press Behaves Like a Tab in JavaScript
Jquery Slider, How to Make "Step" Size Change
Vue.Js Get Selected Option on @Change
Formdata.Append("Key", "Value") Is Not Working
How to Automatically Set the Focus to a Textbox When a Web Page Loads
How to Use JSON File in HTML Code
How to Handle <Tab> in Textarea
How to Auto-Submit an Upload Form When a File Is Selected
HTML Anchor Link - Href and Onclick Both
Remove Whitespaces Inside a String in JavaScript
How to Dynamically Scale Text Size Based on Browser Width
Access Event to Call Preventdefault from Custom Function Originating from Onclick Attribute of Tag
JSON Object Undefined in Internet Explorer 8
How to Get the HTML of a Div on Another Page with Jquery Ajax