'React custom mount hook
I always forget to add empty dependencies array in my useEffect
hook in React to run the effect only once. That's why I decided to use a custom hook with a clear intention in it's name to run only once on mount. But I don't understand why is it running twice?
import React from "react";
import "./styles.css";
function useOnMount(f) {
const isMountedRef = React.useRef(false);
console.log("ref1", isMountedRef.current);
if (!isMountedRef.current) {
f();
isMountedRef.current = true;
console.log("ref2", isMountedRef.current);
}
}
export default function App() {
useOnMount(() => {
console.log("useOnMount");
});
return <div>Hello useOnMount</div>;
}
Here's the output:
ref1 false
useOnMount
ref2 true
ref1 false
useOnMount
ref2 true
I use ref
hook to keep mutable flag between renders. But I can't understand why isMountedRef.current
is true
on the first render and reverts to false
on the second render 🤔
Solution 1:[1]
You keep forgetting the dependencies array? Why not just remember to write it once in your custom hook?
function useOnce (once) {
return useEffect(once, [])
}
Using it in your App
-
function App () {
useOnce(_ => console.log("useOnce"))
return <div>hello</div>
}
Here's a demo -
const { useEffect, useState } = React
function useOnce (once) {
return useEffect(once, [])
}
function App () {
useOnce(_ => alert("Wake up. It's time to make candy!"))
const [candy, setCandy] =
useState(0)
const earn =
<button onClick={_ => setCandy(candy + 1)}>
Make candy
</button>
const spend =
<button onClick={_ => setCandy(candy - 10)}>
Buy Chocolate (10)
</button>
return <div>
<b>Candy Box</b>
<p>Alert will only appear one time after component mounts</p>
<p>
{earn}
{(candy >= 10) && spend}
</p>
<p>You have {candy} candies</p>
</div>
}
ReactDOM.render(<App/>, document.body)
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/16.13.1/umd/react.production.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/16.13.1/umd/react-dom.production.min.js"></script>
Solution 2:[2]
This one works for me and doesn't generate react-hooks/exhaustive-deps
warnings.
function useOnce (once) {
const [ res ] = useState(once)
return res
}
Usage:
function App () {
useOnce(() => {
console.log("useOnce")
// use any App props or result of other hooks
...
})
return <div>hello</div>
}
It was recommended here for another task but it works well as a componentDidMount
replacement. Any parameters may be used inside the function. Just make sure you really don't need to monitor changes of props.
Sources
This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.
Source: Stack Overflow
Solution | Source |
---|---|
Solution 1 | |
Solution 2 | Dron007 |