useEffect hook - dependencies - re-render issues - reactjs

The first case:
_Let's say I have a prop that is in redux state or in parent state.
_I do not want the useEffect to fire whenever this prop changes,
_But I do need to use the prop within the useEffect.
_React warns me to add the prop to the dependency array, but if I do so, then the useEffect will fire again.
The second case:
_I am using a function within a useEffect,
_But the function is also needed elsewhere.
_Don't want to duplicate the function code.
_React wants me to add the function to the dependency array, but I don't want the useEffect to fire every time that function reference changes.

While working with useEffect you should think about closures. If any props or local variable is used inside the useEffect then it is advised to include it inside your dependency array else you will be working with the stale data due to the closure.
Here I present a use case. Inside ComponentUsingRef we are using ref which works as the container. You can find more about it at https://reactjs.org/docs/hooks-reference.html#useref
Advantage of this approach is that you wont be bound to memoize fn in
your parent component. You will always be using latest value of
function and in the given case it won't even cause firing of useEffect as it won't be on your dependency
const Component=({fn})=>{
useEffect(()=>{
fn()
},[fn])
.....
.....
return <SomeComponent {...newProps}/>
}
const ComponentUsingRef=({fn}){
const fnRef = useRef(fn)
fnRef.current = fn // so that our ref container contains the latest value
useEffect(()=>{
fn.current()
},[ ])
.....
.....
return <SomeComponent {...newProps}/>
}
If you want to use abstraction then you should extract your logic inside the custom hook

If the only issue is the warning then don't worry about it, you can simply disable the rule for your effect and omit the prop from the dependency array if it's the only prop being used in the effect. For effects with multiple dependencies, you can use useRef to store the latest value of the prop without firing the effect, like this:
const Comp = ({someProp, ...props}) => {
const myProp = useRef(someProp);
myProp.current = someProp;
// this effect won't fire when someProp is changed
React.useEffect(() => {
doSomething(myProp.current, props.a, props.b);
}, [props.a, props.b, myProp]);
}
For your second case, I'd probably put that function in a separate file and import it in the components where it'll be used, however, you can use the same pattern for your second case.

To ignore the warnings react gives you, you can disable them by adding // eslint-disable-next-line react-hooks/exhaustive-deps to the useEffect code. You can read more about this in the Rules of Hooks section in the React documentation. These rules are included in the eslint-plugin-react-hooks package.
To prevent the useEffect from firing everytime the function reference changes, you can use useCallback. The useCallback hook will store the reference to the function instead of the function itself. The reference of the function will only be updated when one of the dependencies of the function is updated. If you don't want the function reference to be updated ever, you can leave the dependency array empty in the same way as the dependency array of the useEffect hook.
Here is a working example of both:
import React, { useState, useEffect, useCallback } from "react";
import "./styles.css";
export default function App() {
const [value, setValue] = useState(0);
const wrappedFunction = useCallback(
(caller) => {
const newValue = value + 1;
setValue(newValue);
console.log(`Firing function from ${caller}: ${newValue}`);
},
[value]
);
useEffect(() => {
console.log(`Logging from useEffect: ${value}`);
wrappedFunction("useEffect");
// eslint-disable-next-line react-hooks/exhaustive-deps
}, []);
return (
<div className="App">
<h1>Hello CodeSandbox</h1>
<div>{value}</div>
<button onClick={() => wrappedFunction("button click")}>
Fire function
</button>
<h2>Start editing to see some magic happen!</h2>
</div>
);
}

Refer this link for the First Problem Sandbox
Here we are just using a state variable to hold the initial value, whether its coming from the Parent or Store, so Even if the value change inside the Parent or store, the child will still be on the older value, until unless it updates it using useEffect.
Parent
export default function App() {
const [state, setState] = useState("monika");
return (
<div className="App">
<h1>My Name is {state}</h1>
<button
onClick={() => {
setState("Rohan");
}}
>
change name
</button>
<First username={state} />
</div>
);
}
Child
export default function First({ username }) {
const [name, setName] = useState(username);
return (
<div>
<h1>My Name is {name}</h1>
</div>
);
}

Related

Best practices for callback functions [duplicate]

What is the main difference between useCallback, useMemo and useEffect?
Give examples of when to use each of them.
A short explanation.
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".
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.
useMemo
It will run on every render, but with cached values. It will only use new values when certain dependencies change. It's used for optimization when you have expensive computations. Here is also a good answer that explains it.
useEffect() will let you create side effects on your components based on the dependencies you send to it.
function Example() {
const [count, setCount] = React.useState(0);
React.useEffect(() => {
document.title = `You clicked ${count} times`;
}, [count]);
return (
<div>
<p>You clicked {count} times</p>
<button onClick={() => setCount(count + 1)}>
Click me
</button>
</div>
);
}
ReactDOM.render(<Example />, document.getElementById('root'))
<script src="https://unpkg.com/react#16.8.0/umd/react.development.js"></script>
<script src="https://unpkg.com/react-dom#16.8.0/umd/react-dom.development.js"></script>
<div id="root"></div>
The example above is taken from the documentation of React. You can see that each time you click the button it will trigger an update on the count field (using setCount()) and then, the effect that depends on the count variable will trigger an update on the title of the page.
useCallback() will return a memoized callback. Normally, if you have a child component that receives a function prop, at each re-render of the parent component, this function will be re-executed; by using useCallback() you ensure that this function is only re-executed when any value on it's dependency array changes.
function ExampleChild({ callbackFunction }) {
const [value, setValue] = React.useState(0);
React.useEffect(() => {
setValue(value + 1)
}, [callbackFunction]);
return (<p>Child: {value}</p>);
}
function ExampleParent() {
const [count, setCount] = React.useState(0);
const [another, setAnother] = React.useState(0);
const countCallback = React.useCallback(() => {
return count;
}, [count]);
return (
<div>
<ExampleChild callbackFunction={countCallback} />
<button onClick={() => setCount(count + 1)}>
Change callback
</button>
<button onClick={() => setAnother(another + 1)}>
Do not change callback
</button>
</div>
)
}
ReactDOM.render(<ExampleParent />, document.getElementById('root'));
<script src="https://unpkg.com/react#16.8.0/umd/react.development.js"></script>
<script src="https://unpkg.com/react-dom#16.8.0/umd/react-dom.development.js"></script>
<div id="root"></div>
useMemo() will return a memoized value that is the result of the passed parameter. It means that useMemo() will make the calculation for some parameter once and it will then return the same result for the same parameter from a cache.
This is very useful when you need to process a huge amount of data.
function ExampleChild({ value }) {
const [childValue, setChildValue] = React.useState(0);
React.useEffect(() => {
setChildValue(childValue + 1);
}, [value])
return <p>Child value: {childValue}</p>;
}
function ExampleParent() {
const [value, setValue] = React.useState(0);
const heavyProcessing = () => {
// Do some heavy processing with the parameter
console.log(`Cached memo: ${value}`);
return value;
};
const memoizedResult = React.useMemo(heavyProcessing, [value]);
return (
<div>
<ExampleChild value={memoizedResult} />
<button onClick={() => setValue(value + 1)}>
Change memo
</button>
</div>
)
}
ReactDOM.render(<ExampleParent />, document.getElementById('root'));
<script src="https://unpkg.com/react#16.8.0/umd/react.development.js"></script>
<script src="https://unpkg.com/react-dom#16.8.0/umd/react-dom.development.js"></script>
<div id="root"></div>
Most minimal explanation:
useEffect:
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 in case of no dependency:
useEffect(() => {
// execute when component has mounted
() => {
// execute when component will unmount
}
}, []);
useCallback:
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.
useMemo
Whenever you have a value that is depending on certain state. Same as useCallback, useMemo is ment to reduce reassignments for performance optimization.
const myValue = useMemo(() => {
// return calculated value
}, [state]);
Same as useCallback, myValue is only assigned when state is changing and therefore will reduce compute time. Otherwise myValue will be reassigned on every render.
!Trick to mimick componentWillMount lifecycle
useMemo(() => {
// execute componentWillMount logic
]}, []);
Since useEffect is called after the first render and then on every dependency change. It never runs before the first render.
useMemo is executed inline with your JS therefore will be executed before it reaches your Components return statement.
!NOTE: functions with useCallback and values with useMemo can be used as dependency in useCallback, useMemo and useEffect. It is highly recommended to use these hooks in order to have a well structured and readable flow of state in your component. These hooks do not trigger a render. Only useState and useReducer do!
If you want to keep state that doesnt trigger a rerender or any of the above explained hooks you can use useRef. useRef will keep a value consistent over renders without triggering any state dependent value or effect.
It's all well and good to know when to use the functions, but I wanted to know what the actual difference was between them! Here is what I found:
useMemo runs the code immediately, so the return value is available to code that comes after after it. This means it runs before the first render, so any useRef you are using to access HTML components won't be available on the initial run. (But you can add ref.current to the useMemo dependencies to have the useMemo code run again after the first render, when the useRef value has become available). Since the return value is available to code directly following it, this is why it is recommended for complex calculations that don't need to re-run on each render, as having the return value available immediately saves you from having to mess with the state to store the value now and access it later - just grab the return value of useMemo() and use it right away.
useEffect does not run immediately but runs after the first render. This means any useRef values referring to HTML elements will be valid on the first run. Since it runs after all the code in your function has finished and rendered, there is no point having a return value as there is no further code running that could use it. The only way useEffect code can do anything visible is by either changing the state to cause a re-render, or modifying the DOM directly.
useCallback is the same as useMemo except that it remembers the function itself rather than its return value. This means a useCallback function does not run immediately but can be run later (or not run at all), while useMemo runs its function immediately and just saves its return value for later use. Unlike useMemo this is not good for complex calculations as the code will run again every time it is used. If you ever pass a callback function as a prop to another component in your render function, make sure you're passing the return value of useCallback. If you make your callback function like const onClick = () => { ... } or in JSX as onClick={() => { ... }} then every render you will get a new instance of the function, so the child component will always re-render as it thinks you're changing the callback to a different function on every single render. But useCallback returns the same instance of the function each time, so the child function may skip the render completely if nothing else changed, making your app more responsive.
For example, if we pass the same function to both useMemo and useCallback:
let input = 123;
const output = useMemo(() => {
return input + 1;
}, [
input,
]);
// The above function has now run and its return value is available.
console.log( output ); // 124
input = 125; // no effect as the function has already run
console.log( output ); // 124
let input = 123;
const output = useCallback(() => {
return input + 1;
}, [
input,
]);
// The above function has not run yet but we can run it now.
console.log( output() ); // 124
input = 125; // changes the result as the function is running again
console.log( output() ); // 126
Here, useCallback has remembered the function and will keep returning the original function on future renders until the dependencies change, while useMemo actually runs the function immediately and just remembers its return value.
Both useCallback() and useMemo() provide return values that can be used immediately, while useEffect() does not because its code does not run until much later, after the render has completed.
useEffect
Gets called when the component mounts, unmounts and any of it's dependencies change.
Can be used to get data when component is mounted, subscribe and unsubscribe to event streams when component mounts and unmounts (think rxjs).
const [userId, updateUser] = useState(1);
useEffect(()=>{
//subscription
const sub = getUser(userId).subscribe(user => user);
// cleanup
return () => {
sub.unsubscribe();
}
},[userId]) // <-- Will get called again when userId changes
Can also be used for onetime method call which require no cleanup
useEffect(()=>{
oneTimeData();
},[]); // pass empty array to prevent being called multiple times
useCallback
Got functions that you don't want to be re-created on every component render?
Want a function that isn't called on component mount or unmount?
Use useCallback
const [val, updateValue] = useState(0);
const Compo = () => {
/* inc and dec will be re-created on every component render.
Not desirable a function does very intensive work.
*/
const inc = () => updateValue(x => x + 1);
const dec = () => updateValue(x => x - 1);
return render() {
<Comp1 onClick={inc} />
<Comp2 onClick={dec} />
}
}
useCallback to the rescue
const [val, updateValue] = useState(0);
const Compo = () => {
const callbackInc = useCallback(() => {
setCount(currentVal => currentVal + 1);
}, []);
const callbackDec = useCallback(() => {
setCount(currentVal => currentVal - 1);
}, []);
return render() {
<Comp1 onClick={callbackInc} />
<Comp2 onClick={callbackDec} />
}
}
If the argument passed to setCount isn't a function, then the variables you would want useCallback to 'watch' out for must be specified in the dependencies array less there will be no change effect.
const callbackInc = useCallback(() => {
setCount(val + 1); // val is an 'outside' variable therefore must be specified as a dependency
}, [val]);
useMemo
Doing heavy processing and want to memoize (cache) the results? Use useMemo
/*
heavyProcessFunc will only be called again when either val or val2 changes
*/
const result = useMemo(heavyProcessFunc(val, val2),[val,val2])
All of these hooks have the same goal: avoiding redundant component rebuilds (and re-execution of the stuff inside the hooks).
useEffect returns nothing (void) and thus is suitable for such cases.
useCallback returns a function which will be used later in the component. Unlike normal function declaration, it will not trigger component rebuild unless its dependencies change.
useMemo is just another flavour of useCallback.
Here is the best explanation I've seen so far.

Problem with useCallback not working as expected

I'm expecting that when I pass a function wrapped in useCallback as an attribute to another function, that the function will still work, but the function will not be re-created on each call.
I have this problem in a large application, but I've made the problem into a small, reproducible example that I can share here.
Below is the code that I'm struggling with. I have commented out my attempt at working with useCallback that is not working as expected. When I don't use useCallback (the non-commented out code), the app toggles the theme as I expect.
I have the simple example in codesandbox at this URL:
https://codesandbox.io/s/github/pkellner/callback-theme-toggle
If I un-comment the useCallback line, the theme toggles once, then never toggles again.
My expectation is that with the useCallback code that the theme will toggle and appMenu.js will not get re-rendered on every theme toggle click.
Here is the /pages/index.js
import {useCallback, useContext} from "react";
import AppMenu from "../src/AppMenu";
import { ThemeContext, ThemeProvider } from "../src/ThemeContext";
function Inner() {
const { toggleTheme, darkTheme } = useContext(ThemeContext);
return (
<div>
<h1>HOME</h1>
{/*<AppMenu toggleTheme={useCallback(toggleTheme,[])} />*/}
<AppMenu toggleTheme={toggleTheme} />
<h2>darkTheme: {darkTheme === true ? "true" : "false"}</h2>
</div>
);
}
export default function Home() {
return (
<ThemeProvider>
<Inner />
</ThemeProvider>
);
}
There's a few points to address here.
When you useCallback, you create a closure around whatever dependencies it has. The callback needs to be recreated whenever one of those dependencies changes. The "meat" of your callback is this:
const toggleTheme = () => {
setDarkTheme(!darkTheme);
};
Which means you have two dependencies - setDarkTheme (which is functionally stable), and darkTheme (which changes). So, when you wrap this in a useCallback and don't declare darkTheme as a dependency, the callback closures over the initial value of darkTheme, and always then uses that to toggle on (which is why the toggle works one time, but then never works again).
But good news! You can get rid of your dependency on darkTheme by using the callback version of setState -
const toggleTheme = () => {
setDarkTheme(prev => !prev);
};
Boom! Now you have a functionally stable callback! You can safely wrap this into a useCallback hook with no dependencies, and it will work as you had intended!
But... you'll notice that even if you do that, AppMenu will always re-render when the them changes. Toggling the theme updates your Context object, and:
All consumers that are descendants of a Provider will re-render whenever the Provider’s value prop changes.
(from the doco of Context).
Inner is a consumer of the context (via the useContext hook), and the parent of AppMenu - which means they both re-render when the context changes.
When you do
useCallback(toggleTheme,[])
This tells React that the value to use at that point is the value of toggleTheme when the component mounts. Since the dependency array is empty, the callback never changes.
When the component mounts, toggleTheme in context closes over the state value at that point:
const toggleTheme = () => {
console.log(`useTheme:toggleTheme:${darkTheme}`);
setDarkTheme(!darkTheme); // <---- reference to state
};
darkTheme is false at that point. Although the value passed down by useContext changes when the component re-renders, because you're using useCallback with an empty dependency array, the value returned by useCallback remains the same - it refers to the initial toggleTheme from context, where darkTheme is its initial value (so further clicks don't appear to produce a change, because you're setting state to the same state as it is currently).
useCallback doesn't make any sense here; just use the value returned by useContext so that it always has the most up-to-date state value, instead of adding extra complication. The function is re-created by context, not by your use (or lack thereof) of useCallback.
If you want AppMenu to not re-render, then you should memoize it - and you should fix the context's toggleTheme so that the function it passes down doesn't depend on a reference to a (possibly stale) state value in the closure.
const toggleTheme = () => {
setDarkTheme(theme => !theme);
};
function Inner() {
const { toggleTheme, darkTheme } = useContext(ThemeContext);
const menu = useMemo(() => <AppMenu toggleTheme={toggleTheme} />, []);
return (
<div>
<h1>HOME</h1>
{menu}
<h2>darkTheme: {darkTheme === true ? "true" : "false"}</h2>
</div>
);
}

Question regarding benefit of React useCallback hook [duplicate]

As said in docs, useCallback
Returns a memoized callback.
Pass an inline callback and an array of inputs. useCallback will return a memoized version of the callback that only changes if one of the inputs has changed. This is useful when passing callbacks to optimized child components that rely on reference equality to prevent unnecessary renders (e.g. shouldComponentUpdate).
const memoizedCallback = useCallback(
() => {
doSomething(a, b);
},
[a, b],
);
But how does it work and where is the best to use it in React?
P.S. I think visualisation with codepen example will help everyone to understand it better. Explained in docs.
This is best used when you want to prevent unnecessary re-renders for better performance.
Compare these two ways of passing callbacks to child components taken from React Docs:
1. Arrow Function in Render
class Foo extends Component {
handleClick() {
console.log('Click happened');
}
render() {
return <Button onClick={() => this.handleClick()}>Click Me</Button>;
}
}
2. Bind in Constructor (ES2015)
class Foo extends Component {
constructor(props) {
super(props);
this.handleClick = this.handleClick.bind(this);
}
handleClick() {
console.log('Click happened');
}
render() {
return <Button onClick={this.handleClick}>Click Me</Button>;
}
}
Assuming <Button> is implemented as a PureComponent, the first way will cause <Button> to re-render every time <Foo> re-renders because a new function is created in every render() call. In the second way, the handleClick method is only created once in <Foo>'s constructor and reused across renders.
If we translate both approaches to functional components using hooks, these are the equivalents (sort of):
1. Arrow Function in Render -> Un-memoized callback
function Foo() {
const handleClick = () => {
console.log('Click happened');
}
return <Button onClick={handleClick}>Click Me</Button>;
}
2. Bind in Constructor (ES2015) -> Memoized callbacks
function Foo() {
const memoizedHandleClick = useCallback(
() => console.log('Click happened'), [],
); // Tells React to memoize regardless of arguments.
return <Button onClick={memoizedHandleClick}>Click Me</Button>;
}
The first way creates callbacks on every call of the functional component but in the second way, React memoizes the callback function for you and the callback is not created multiple times.
Hence in the first case if Button is implemented using React.memo it will always re render (unless you have some custom comparison function) because the onClick prop is different each time, in the second case, it won't.
In most cases, it's fine to do the first way. As the React docs state:
Is it OK to use arrow functions in render methods? Generally speaking,
yes, it is OK, and it is often the easiest way to pass parameters to
callback functions.
If you do have performance issues, by all means, optimize!
useCallback and useMemo are an attempt to bypass weak spots that come with the functional programming approach chosen with React hooks. In Javascript, each entity, no matter if it is a function, variable, or whatever, is created into the memory when the execution will enter the function's code block. This is a big issue for a React that will try to detect if the component needs to be rendered. The need for rerendering is deducted based on input props and contexts. Let's see a simple example without useCallback.
const Component = () => {
const [counter, setCounter] = useState(0);
const handleClick = () => {
setCounter(counter + 1);
}
return <div>
Counter:{counter}<br/>
<button onClick={handleClick}>+1</button>
</div>
}
Note that the handleClick -function instance will be created on each function call inside the block, so the event handler's address on each call will be different. The React framework will always see the event handler as changed because of this. In the example above, React will think handleClick as a new value on each call. It simply has no tools to identify it as the same call.
What useCallback does, it internally stores the first introduced version of the function and returns it to the caller, if the listed variables have not changed.
const Component = () => {
const [counter, setCounter] = useState(0);
const handleClick = useCallback(() => {
setCounter(counter + 1);
}, [])
return <div>
Counter:{counter}<br/>
<button onClick={handleClick}>+1</button>
</div>
}
Now, with the code above, React will identify the handleClick -event handler as the same, thanks to useCallback -function call. It will always return the same instance of function and React component rendering mechanism will be happy.
Storing the function internally by the useCallback will end up with a new problem. The stored instance of the function call will not have direct access to the variables of the current function call. Instead, it will see variables introduced in the initial closure call where the stored function was created. So the call will not work for updated variables. Thats why you need need tell if some used variables have changed. So that the useCallback will store the current function call instance as a new stored instance. The list of variables as the second argument of the useCallback is listing variables for this functionality. In our example, we need to tell to useCallback -function that we need to have a fresh version of counter -variable on each call. If we will not do that, the counter value after the call will be always 1, which comes from the original value 0 plus 1.
const Component = () => {
const [counter, setCounter] = useState(0);
const handleClick = useCallback(() => {
setCounter(counter + 1);
}, [counter])
return <div>
Counter:{counter}<br/>
<button onClick={handleClick}>+1</button>
</div>
}
Now we have a working version of the code that will not rerender on every call.
It is good to notice that the useState -call is here just for the same reason. Function block does not have an internal state, so hooks are using useState, useCallback and useMemo to mimic the basic functionality of classes. In this sense, functional programming is a big step back in history closer to procedural programming.
useMemo is the same kind of mechanism as useCallback but for other objects and variables. With it, you can limit the need for component rerender, as the useMemo -function will return the same values on each function call if the listed fields have not changed.
This part of the new React hooks -approach is definitely the weakest spot of the system. useCallback is pretty much counterintuitive and really error-prone. With useCallback-calls and dependencies, it is too easy to end up chasing internal loops. This caveat we did not have with the React Class approach.
The original approach with classes was more efficient after all. The useCallback will reduce the need to rerender, but it regenerates the function again every time when some of its dependant variables will change, and matching if the variables have changes itself will make overhead. This may cause more rerenders than necessary. This is not the case with React classes.
I've made a small example to help others understand better how it behaves. You can run the demo here or read the code bellow:
import React, { useState, useCallback, useMemo } from 'react';
import { render } from 'react-dom';
const App = () => {
const [state, changeState] = useState({});
const memoizedValue = useMemo(() => Math.random(), []);
const memoizedCallback = useCallback(() => console.log(memoizedValue), []);
const unMemoizedCallback = () => console.log(memoizedValue);
const {prevMemoizedCallback, prevUnMemoizedCallback} = state;
return (
<>
<p>Memoized value: {memoizedValue}</p>
<p>New update {Math.random()}</p>
<p>is prevMemoizedCallback === to memoizedCallback: { String(prevMemoizedCallback === memoizedCallback)}</p>
<p>is prevUnMemoizedCallback === to unMemoizedCallback: { String(prevUnMemoizedCallback === unMemoizedCallback) }</p>
<p><button onClick={memoizedCallback}>memoizedCallback</button></p>
<p><button onClick={unMemoizedCallback}>unMemoizedCallback</button></p>
<p><button onClick={() => changeState({ prevMemoizedCallback: memoizedCallback, prevUnMemoizedCallback: unMemoizedCallback })}>update State</button></p>
</>
);
};
render(<App />, document.getElementById('root'));
An event handler gets recreated and assigned a different address on every render by default, resulting in a changed ‘props’ object. Below, button 2 is not repeatedly rendered as the ‘props’ object has not changed. Notice how the entire Example() function runs till completion on every render.
const MyButton = React.memo(props=>{
console.log('firing from '+props.id);
return (<button onClick={props.eh}>{props.id}</button>);
});
function Example(){
const [a,setA] = React.useState(0);
const unmemoizedCallback = () => {};
const memoizedCallback = React.useCallback(()=>{},[]); // don’t forget []!
setTimeout(()=>{setA(a=>(a+1));},3000);
return (<React.Fragment>
<MyButton id="1" eh={unmemoizedCallback}/>
<MyButton id="2" eh={memoizedCallback}/>
<MyButton id="3" eh={()=>memoizedCallback}/>
</React.Fragment>);
}
ReactDOM.render(<Example/>,document.querySelector("div"));

How to Unmount React Functional Component?

I've built several modals as React functional components. They were shown/hidden via an isModalOpen boolean property in the modal's associated Context. This has worked great.
Now, for various reasons, a colleague needs me to refactor this code and instead control the visibility of the modal at one level higher. Here's some sample code:
import React, { useState } from 'react';
import Button from 'react-bootstrap/Button';
import { UsersProvider } from '../../../contexts/UsersContext';
import AddUsers from './AddUsers';
const AddUsersLauncher = () => {
const [showModal, setShowModal] = useState(false);
return (
<div>
<UsersProvider>
<Button onClick={() => setShowModal(true)}>Add Users</Button>
{showModal && <AddUsers />}
</UsersProvider>
</div>
);
};
export default AddUsersLauncher;
This all works great initially. A button is rendered and when that button is pressed then the modal is shown.
The problem lies with how to hide it. Before I was just setting isModalOpen to false in the reducer.
When I had a quick conversation with my colleague earlier today, he said that the code above would work and I wouldn't have to pass anything into AddUsers. I'm thinking though that I need to pass the setShowModal function into the component as it could then be called to hide the modal.
But I'm open to the possibility that I'm not seeing a much simpler way to do this. Might there be?
To call something on unmount you can use useEffect. Whatever you return in the useEffect, that will be called on unmount. For example, in your case
const AddUsersLauncher = () => {
const [showModal, setShowModal] = useState(false);
useEffect(() => {
return () => {
// Your code you want to run on unmount.
};
}, []);
return (
<div>
<UsersProvider>
<Button onClick={() => setShowModal(true)}>Add Users</Button>
{showModal && <AddUsers />}
</UsersProvider>
</div>
);
};
Second argument of the useEffect accepts an array, which diff the value of elements to check whether to call useEffect again. Here, I passed empty array [], so, it will call useEffect only once.
If you have passed something else, lets say, showModal in the array, then whenever showModal value will change, useEffect will call, and will call the returned function if specified.
If you want to leave showModal as state variable in AddUsersLauncher and change it from within AddUsers, then yes, you have to pass the reference of setShowModal to AddUsers. State management in React can become messy in two-way data flows, so I would advise you to have a look at Redux for storing and changing state shared by multiple components

When to use useCallback, useMemo and useEffect?

What is the main difference between useCallback, useMemo and useEffect?
Give examples of when to use each of them.
A short explanation.
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".
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.
useMemo
It will run on every render, but with cached values. It will only use new values when certain dependencies change. It's used for optimization when you have expensive computations. Here is also a good answer that explains it.
useEffect() will let you create side effects on your components based on the dependencies you send to it.
function Example() {
const [count, setCount] = React.useState(0);
React.useEffect(() => {
document.title = `You clicked ${count} times`;
}, [count]);
return (
<div>
<p>You clicked {count} times</p>
<button onClick={() => setCount(count + 1)}>
Click me
</button>
</div>
);
}
ReactDOM.render(<Example />, document.getElementById('root'))
<script src="https://unpkg.com/react#16.8.0/umd/react.development.js"></script>
<script src="https://unpkg.com/react-dom#16.8.0/umd/react-dom.development.js"></script>
<div id="root"></div>
The example above is taken from the documentation of React. You can see that each time you click the button it will trigger an update on the count field (using setCount()) and then, the effect that depends on the count variable will trigger an update on the title of the page.
useCallback() will return a memoized callback. Normally, if you have a child component that receives a function prop, at each re-render of the parent component, this function will be re-executed; by using useCallback() you ensure that this function is only re-executed when any value on it's dependency array changes.
function ExampleChild({ callbackFunction }) {
const [value, setValue] = React.useState(0);
React.useEffect(() => {
setValue(value + 1)
}, [callbackFunction]);
return (<p>Child: {value}</p>);
}
function ExampleParent() {
const [count, setCount] = React.useState(0);
const [another, setAnother] = React.useState(0);
const countCallback = React.useCallback(() => {
return count;
}, [count]);
return (
<div>
<ExampleChild callbackFunction={countCallback} />
<button onClick={() => setCount(count + 1)}>
Change callback
</button>
<button onClick={() => setAnother(another + 1)}>
Do not change callback
</button>
</div>
)
}
ReactDOM.render(<ExampleParent />, document.getElementById('root'));
<script src="https://unpkg.com/react#16.8.0/umd/react.development.js"></script>
<script src="https://unpkg.com/react-dom#16.8.0/umd/react-dom.development.js"></script>
<div id="root"></div>
useMemo() will return a memoized value that is the result of the passed parameter. It means that useMemo() will make the calculation for some parameter once and it will then return the same result for the same parameter from a cache.
This is very useful when you need to process a huge amount of data.
function ExampleChild({ value }) {
const [childValue, setChildValue] = React.useState(0);
React.useEffect(() => {
setChildValue(childValue + 1);
}, [value])
return <p>Child value: {childValue}</p>;
}
function ExampleParent() {
const [value, setValue] = React.useState(0);
const heavyProcessing = () => {
// Do some heavy processing with the parameter
console.log(`Cached memo: ${value}`);
return value;
};
const memoizedResult = React.useMemo(heavyProcessing, [value]);
return (
<div>
<ExampleChild value={memoizedResult} />
<button onClick={() => setValue(value + 1)}>
Change memo
</button>
</div>
)
}
ReactDOM.render(<ExampleParent />, document.getElementById('root'));
<script src="https://unpkg.com/react#16.8.0/umd/react.development.js"></script>
<script src="https://unpkg.com/react-dom#16.8.0/umd/react-dom.development.js"></script>
<div id="root"></div>
Most minimal explanation:
useEffect:
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 in case of no dependency:
useEffect(() => {
// execute when component has mounted
() => {
// execute when component will unmount
}
}, []);
useCallback:
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.
useMemo
Whenever you have a value that is depending on certain state. Same as useCallback, useMemo is ment to reduce reassignments for performance optimization.
const myValue = useMemo(() => {
// return calculated value
}, [state]);
Same as useCallback, myValue is only assigned when state is changing and therefore will reduce compute time. Otherwise myValue will be reassigned on every render.
!Trick to mimick componentWillMount lifecycle
useMemo(() => {
// execute componentWillMount logic
]}, []);
Since useEffect is called after the first render and then on every dependency change. It never runs before the first render.
useMemo is executed inline with your JS therefore will be executed before it reaches your Components return statement.
!NOTE: functions with useCallback and values with useMemo can be used as dependency in useCallback, useMemo and useEffect. It is highly recommended to use these hooks in order to have a well structured and readable flow of state in your component. These hooks do not trigger a render. Only useState and useReducer do!
If you want to keep state that doesnt trigger a rerender or any of the above explained hooks you can use useRef. useRef will keep a value consistent over renders without triggering any state dependent value or effect.
It's all well and good to know when to use the functions, but I wanted to know what the actual difference was between them! Here is what I found:
useMemo runs the code immediately, so the return value is available to code that comes after after it. This means it runs before the first render, so any useRef you are using to access HTML components won't be available on the initial run. (But you can add ref.current to the useMemo dependencies to have the useMemo code run again after the first render, when the useRef value has become available). Since the return value is available to code directly following it, this is why it is recommended for complex calculations that don't need to re-run on each render, as having the return value available immediately saves you from having to mess with the state to store the value now and access it later - just grab the return value of useMemo() and use it right away.
useEffect does not run immediately but runs after the first render. This means any useRef values referring to HTML elements will be valid on the first run. Since it runs after all the code in your function has finished and rendered, there is no point having a return value as there is no further code running that could use it. The only way useEffect code can do anything visible is by either changing the state to cause a re-render, or modifying the DOM directly.
useCallback is the same as useMemo except that it remembers the function itself rather than its return value. This means a useCallback function does not run immediately but can be run later (or not run at all), while useMemo runs its function immediately and just saves its return value for later use. Unlike useMemo this is not good for complex calculations as the code will run again every time it is used. If you ever pass a callback function as a prop to another component in your render function, make sure you're passing the return value of useCallback. If you make your callback function like const onClick = () => { ... } or in JSX as onClick={() => { ... }} then every render you will get a new instance of the function, so the child component will always re-render as it thinks you're changing the callback to a different function on every single render. But useCallback returns the same instance of the function each time, so the child function may skip the render completely if nothing else changed, making your app more responsive.
For example, if we pass the same function to both useMemo and useCallback:
let input = 123;
const output = useMemo(() => {
return input + 1;
}, [
input,
]);
// The above function has now run and its return value is available.
console.log( output ); // 124
input = 125; // no effect as the function has already run
console.log( output ); // 124
let input = 123;
const output = useCallback(() => {
return input + 1;
}, [
input,
]);
// The above function has not run yet but we can run it now.
console.log( output() ); // 124
input = 125; // changes the result as the function is running again
console.log( output() ); // 126
Here, useCallback has remembered the function and will keep returning the original function on future renders until the dependencies change, while useMemo actually runs the function immediately and just remembers its return value.
Both useCallback() and useMemo() provide return values that can be used immediately, while useEffect() does not because its code does not run until much later, after the render has completed.
useEffect
Gets called when the component mounts, unmounts and any of it's dependencies change.
Can be used to get data when component is mounted, subscribe and unsubscribe to event streams when component mounts and unmounts (think rxjs).
const [userId, updateUser] = useState(1);
useEffect(()=>{
//subscription
const sub = getUser(userId).subscribe(user => user);
// cleanup
return () => {
sub.unsubscribe();
}
},[userId]) // <-- Will get called again when userId changes
Can also be used for onetime method call which require no cleanup
useEffect(()=>{
oneTimeData();
},[]); // pass empty array to prevent being called multiple times
useCallback
Got functions that you don't want to be re-created on every component render?
Want a function that isn't called on component mount or unmount?
Use useCallback
const [val, updateValue] = useState(0);
const Compo = () => {
/* inc and dec will be re-created on every component render.
Not desirable a function does very intensive work.
*/
const inc = () => updateValue(x => x + 1);
const dec = () => updateValue(x => x - 1);
return render() {
<Comp1 onClick={inc} />
<Comp2 onClick={dec} />
}
}
useCallback to the rescue
const [val, updateValue] = useState(0);
const Compo = () => {
const callbackInc = useCallback(() => {
setCount(currentVal => currentVal + 1);
}, []);
const callbackDec = useCallback(() => {
setCount(currentVal => currentVal - 1);
}, []);
return render() {
<Comp1 onClick={callbackInc} />
<Comp2 onClick={callbackDec} />
}
}
If the argument passed to setCount isn't a function, then the variables you would want useCallback to 'watch' out for must be specified in the dependencies array less there will be no change effect.
const callbackInc = useCallback(() => {
setCount(val + 1); // val is an 'outside' variable therefore must be specified as a dependency
}, [val]);
useMemo
Doing heavy processing and want to memoize (cache) the results? Use useMemo
/*
heavyProcessFunc will only be called again when either val or val2 changes
*/
const result = useMemo(heavyProcessFunc(val, val2),[val,val2])
All of these hooks have the same goal: avoiding redundant component rebuilds (and re-execution of the stuff inside the hooks).
useEffect returns nothing (void) and thus is suitable for such cases.
useCallback returns a function which will be used later in the component. Unlike normal function declaration, it will not trigger component rebuild unless its dependencies change.
useMemo is just another flavour of useCallback.
Here is the best explanation I've seen so far.

Resources