useMemo vs useState for React hooks constants - reactjs

Defining a calculated (initialized) constant using React hooks can be performed in two ways that seem functionally equivalent. I don't want to get into the use cases for this, but suffice to say that there are cases where a constant value can be derived from initial props or state that isn't expected to change (think route data, bound dispatch, etc).
First, useState
const [calculatedConstant] = useState(calculateConstantFactory);
Second, useMemo
const calculatedConstant = useMemo(calculateConstantFactory, []);
Both of these seem functionally equivalent, but without reading the source code, I'm not sure which is better in terms of performance or other considerations.
Has anyone done the leg work on this? Which would you use and why?
Also, I know some people will recoil at the assumption that state can be "considered constant". I'm not sure what to tell you there. But even without state, I may want to define a constant within a component that has no state at all, for example, creating a block of JSX that doesn't change.
I could define this outside of the component, but then it's consuming memory, even when the component in question is not instantiated anywhere in the app. To fix this, I would have to create a memoization function and then manually release the internal memoized state. That's an awful lot of hassle for something hooks give us for free.
Edit: Added examples of the approaches talked about in this discussion.
https://codesandbox.io/s/cranky-platform-2b15l

You may rely on useMemo as a performance optimization, not as a semantic guarantee
Meaning, semantically useMemo is not the correct approach; you are using it for the wrong reason. So even though it works as intended now, you are using it incorrectly and it could lead to unpredictable behavior in the future.
useState is only the correct choice if you don't want your rendering to be blocked while the value is being computed.
If the value isn't needed in the first render of the component, you could use useRef and useEffect together:
const calculatedConstant = useRef(null);
useEffect(() => {
calculatedConstant.current = calculateConstantFactory()
}, [])
// use the value in calcaulatedConstant.current
This is the same as initializing an instance field in componentDidMount. And it doesn't block your layout / paint while the factory function is being run. Performance-wise, I doubt any benchmark would show a significant difference.
The problem is that after initializing the ref, the component won't update to reflect this value (which is the whole purpose of a ref).
If you absolutely need the value to be used on the component's first render, you can do this:
const calculatedConstant = useRef(null);
if (!calculatedConstant.current) {
calculatedConstant.current = calculateConstantFactory();
}
// use the value in calculatedConstant.current;
This one will block your component from rendering before the value is set up.
If you don't want rendering to be blocked, you need useState together with useEffect:
const [calculated, setCalculated] = useState();
useEffect(() => {
setCalculated(calculateConstantFactory())
}, [])
// use the value in calculated
Basically if you need the component to re-render itself: use state. If that's not necessary, use a ref.

Related

Do I understand React memoization correctly?

I’ve read the documentation about memoization techniques in React and have a feeling that I understand them correctly.
However, I see projects where useMemo and useCallback are applied in different ways.
I would like to confirm that I’m not missing anything, so I will describe my understanding below. Please, tell me if this makes sense or correct me if I’m wrong :)
useMemo
This hook is used to cache the result of some calculation between re-renders.
const cachedValue = useMemo(() => calculateValue(), dependencies)
It means that after the first run, the cachedValue is saved in memory and returned on subsequent renders without any calculations if dependencies remain the same. The cachedValue object on the will be the same (same reference) and would return true on Object.is().
Memoizing results of expensive computations improves performance because they are not calculated even if the component is re-rendered, but at the cost of running the hook itself. (Not sure about that)
Additionally, it is used to prevent re-renders when passing these values as props to a component memoized with React.memo(Component) or as dependencies to useEffect hook.
useCallback
This hook caches the function definition (not the result of its work) between re-renders.
const cachedHandler = useCallback(() => { doSomething(); }, dependencies);
The function is defined and saved (but not called) in the variable on the first run, and the same definition is returned on subsequent re-renders if dependencies remain the same.
Since the function is not called when this hook is used and no results are cached, there are no immediate performance gains. (Not sure about that)
The benefit is only when this cached function is passed as a prop to a component memoized with React.memo(Component) or as a dependency to useEffect.
When a memoized function is passed as a prop inside an inline function (<Component onSomething={() => cachedHandler()} /> there are no performance gains since a new anonymous function is created and passed every time. (Saw this usage a few times).
React.memo
A component wrapped in React.memo is cached and skips re-renders as long as its props remain the same. There is no need to memoize primitive values (strings, for example), but for objects and functions, you need to apply either useMemo or useCallback hooks.
General notes
A lot of projects and developers wrap values and functions in these hooks prematurely either by convention or as a precaution ("if we memoize everything, there is no need to think about it in the future and the cost is not so high").
The documentation and React developers advise against using these hooks until it is necessary (gives measurable performance gains) as using them is not free and adds cognitive complexity (harder to read and follow the code).
This description does not cover all the cases, but can be applied to most simple cases.

useMemo with Redux

I'm new with Redux, and I'd like to improve performances of my web app as much as possible.
I have a state in redux, that I store in a variable to display it later.
Here is the code :
const metricsState = useSelector((state: MetricsStateObject) => state.MetricsState);
const myMetrics = metricsState.myMetrics;
I saw that useMemo improve performance by not re-render if the data did not mutate.
So I'm wondering if const myMetrics = useMemo(() => metricsState.myMetrics, [metricsState.myMetrics]); is a good practice, or totaly useless ?
Thank you for your time.
useMemo is for high cost computation, you dont want to run each render. Like
const something = useMemo(()=> megaBigArray.reduce((acc,i)=>acc*i,0), [megaBigArray])
or something like that. You only calculate that variable, if megaBigArraychange.
In your case, that code will be run every render anyways, but useSelector should trigger render only, when part of store you are selecting change. So you should be good without it.
Let me talk about the conclusion first, it's totally useless.
why?
because metricsState.myMetrics is just a value-taking process and does not involve expensive calculations.
but useMemo itself consumes a certain amount of calculation.
so I think that belongs to premature optimization
I think #Wraithys answer is not correct here. I'm pretty sure, React will not compute the component with the useSelector if the state of the selector didn't change and the selector also didn't change. This means that you must memoize the selector too, otherwise React and Redux will not be able to optimize the rendering. I think you may have to also memoize the component using React.memo() for it to work fully. Also, it may be best to always state your selectors in a file so you don't have to memoize them, since they are constant.

What's the idea behind useState functionality in ReactJS?

I am learning ReactJS and I understand the benefits of functional programming. However, I am a little concerned about the actual useState approach. If we assume a variable to be changed, then why declaring it const in the first place?
I see that I can simply use let number = 4; and then render it like this <p>{number}</p>. What I cannot do however is to re-render it automatically just by changing it, for example using onClick event like this <p onClick={() => ++number }></p>. Why is it that so? Is there a specific reason I am missing why it was implemented the way it is? I mean why the developers have decided that if the value needs to be re-rendered upon change, then it must be a const value declared with the help of useState functionality?
I am asking this because I am suspecting I am missing some good points behind this and I would like to understand them.
The variable is declared as a const because you are not allowed to set it directly. In React the state itself is immutable. You are just allowed to change it over setState (or setNumber in your case) or with actions if you use redux.
But why is that? It may seem unnecessary cumbersome in the beginning
First of all, if your variable number changes, react has to trigger a rerender.
If the state is mutable, it requires data-binding because if the number is set, it has to update the view.
In javascript, data-binding works for simple objects, but not well for arrays. Vue.js for example, as an alternative that uses two-way data binding, had a lot of trouble in its early versions when dealing with arrays. That's why there are now only seven predefined methods to interact with arrays (which they added later to solve that problem). Vue Js Array Methods
So a simple reason to declare the state as const is that it works better with arrays. And if you watch the example you gave, setNumber(number + 1) is not that much more to write than number++. But setState(newArray) works, and newArray[i] = newElement would not work, because due to javascript limitations this cannot trigger a rerender.
Secondly, it is a nicer design concept. Think of your component as a function, that returns a view to a state. And if the state changes, you get a new view. It simplifies relationships between properties in your component. If you were allowed to change your state while rendering your component, it would create a mess.
The problem is that you're thinking of a functional component as if it was stateful. But it isn't. It's a function and once you run it, that's it.
Take this example:
function useState() {
let value = 1
function setValue(v) {
value = v
}
return [value, setValue]
}
function myFunction () {
const [value, setValue] = useState(); // <----- we use const here
return null
}
Even though we're using const, the value variable only exists within the function, once the function returns that's it. It's the same for components.
The actual value of value is stored in a whole different scope, where useEffect has access to.
Here's a deep dive on how react works internally if you're interested
React works in render cycles, i.e. some state is declared, the DOM (UI) is computed during the "render phase", and then flushed to the actual DOM during the "commit phase".
Within each cycle state is considered constant (const in JS simply means a variable can't be assigned a new value, you could just as easily declare it with let or var instead and react would work the same) but for react's purpose, state is constant during a render cycle. When it is updated via one of the state update methods, react then kicks off another render cycle (update state, compute diff, commit to DOM) and re-renders when necessary.
This process is important and the reason why external state mutations are considered anti-pattern, it goes against the react workflow and leads to buggy code or worse, UI that doesn't update as expected.
React component lifecycle
I cannot do however is to re-render it automatically just by changing
it, for example using onClick event like this <p onClick={() => ++number }></p>. Why is it that so?
React state updates use a process called reconciliation to figure out what changed and what to re-render. In really simplistic terms, when react state is updated it is updated with a new object reference so a shallow object comparison can more quickly detect that the component state updated.
Declaring state and doing ++number simply changes the value but not the reference, and would be considered a state mutation, an anti-pattern in react.

React useContext with "setState"

I recently started using the Context React Api with useContext Hook.
I have observed that when we have a state variable i.e. const someState = useState(state, setState), some
developers pass setSate directly in the provider values and then calling it in children components.
Is that a good practice?
When you do not use context you have to create a handler to "access" setState in a child component.
I am still using handler functions and pass them into the provider values, to import them from context
in children.
Is passing setState in context a good pracice? I still have some doubts since normally you cannot pass setState directly into a component.
Is there any difference in performance or any other drawbacks I should be considering?
Thank you.
Edit: I actually think I got you wrong, but I'm not sure. My reply is valid for the case if you write your own provider that has a state. If you just use a default provider that provides a setter, I would agree with the reply of Amel.
I personally wouldn't do it, but that's more of an opionion. However, like always, it pretty much depends on what goal you want to reach.
A positive aspect of doing it is, that state setters given by useState always stay the same for each rerender. If you pass a custom value, you should avoid that it changes too often because every component listening to the change using useContext would rerender.
I would still prefer to pass a custom object (e.g. coming from a useMemo to avoid unnecessary rerenders) with a callback to set the state. It's easier to extend if you want to provide more stuff in the future.
Something like this (very simplistic example, that of course doesn't make sense like this, it's just for comprehension):
function MyProvider({children}) {
const [state, setState] = useState(0);
const provided = useMemo(() => ({
value: state,
setValue: (value) => setState(value)
}, [value]);
return <MyContext.Provider value={provided}>{children}</MyContext.Provider>;
}
It would be easier to extend without changing code everyhwere where the context is used. However, I still think there is nothing particular bad in passing just the setter, if that is what you want to achive.
If I understood correctly the difference is that in one way the state is set from the parent component and in the other the state is set from the child component.
Sometimes people do it that way to avoid loop rendering with changing the state. There should be no drawbacks, but using handler functions is the regular way to go.
you can use state as variable not as spreaded one
const state = useContext(0);
state[0] //it's the getter for state you can access the values from this
(state[1])(10) //it is setter for state you can set values with is
(state[1])((oldvalues)=>{//write you code here})

React hooks: is `useCallback` not so needed usually?

I am recently refactoring a web app using React Hooks. I encounter a problem regarding useCallback. Based on description of Kent: https://kentcdodds.com/blog/usememo-and-usecallback, useCallback is to pass in identical function reference to sub-components, to avoid re-render of sub-components, so that the performance is better. However, it's used together with React.memo. And as Kent said:
MOST OF THE TIME YOU SHOULD NOT BOTHER OPTIMIZING UNNECESSARY RERENDERS. React is VERY fast and there are so many things I can think of for you to do with your time that would be better than optimizing things like this. In fact, the need to optimize stuff with what I'm about to show you is so rare that I've literally never needed to do it ...
So, my question is: am I right to claim that we do not need to use useCallback usually? except when the callback is expensive to create, using useCallback avoids re-creating the callback for every render.
Say, for a onClick or onChange event handler, 2 lines or less, shall we just not use useCallback to wrap it?
I find the useCallback() is necessary when I don't want the function reference to change. For example, when I'm using React.memo on some child component that should not be re-rendered as a result of a reference change in one of its methods that comes through props.
Example:
In the example below Child1 will always re-render if Parent re-renders, because parentMethod1 will get a new reference on every render. And Child2 will not re-render, because the parentMethod2 will preserve its reference across renders (you can pass a dependency array to make it change and be re-created when new input values come).
Note: Assuming the Child components are being memoized with React.memo()
function Parent() {
const parentMethod1 = () => DO SOMETHING;
const parentMethod2 = useCallback(() => DO SOMETHING,[]);
return(
<React.Fragment>
<Child1
propA=parentMethod1
/>
<Child2
propA=parentMethod2
/>
</React.Fragment>
);
}
On the other hand, if the function is expensive to run, you can memoize its results using the useMemo hook. Then you will only run it when new values come, otherwise it will give you a memoized result from previous calculations using those same values.
https://reactjs.org/docs/hooks-reference.html#usecallback
useCallback
Pass an inline callback and an array of dependencies. useCallback will return a memoized version of the callback that only changes if one of the dependencies has changed. This is useful when passing callbacks to optimized child components that rely on reference equality to prevent unnecessary renders (e.g. shouldComponentUpdate).
useMemo
Pass a “create” function and an array of dependencies. useMemo will only recompute the memoized value when one of the dependencies has changed. This optimization helps to avoid expensive calculations on every render.
I think you are right. From how it's designed, useCallback should be almost useless in React. It can not be used directly to prevent a child render.
What can save a child render is to wrap entire render using a useMemo.
const Title = () => {
...
const child = useMemo(() => {
return <Child a={"Hello World"} />
}, [])
return (
<>
{child}
<div onClick={onClick}>{count}</div>
</>
)
}
The above approach is a bit different than React.memo because it acts directly on the parent Title, instead of Child. But it's more or less answer your question why it's useless, except when you use it as the shortcut for useMemo.
Article explaining this, https://javascript.plainenglish.io/can-usememo-skip-a-child-render-94e61f5ad981
back to useCallback
Now let's go back to see if a callback wrapped with or without useCallback is useful.
<div onClick={onClick}>kk</div>
The only thing it might save is that when it's under reconciliation, onClick (with useCallback) points to the same function instance.
However I don't know if React actually does any optimization at that step. Because assigning a different callback to the attribute might take additional memory and time. But adding a new variable in general takes additional memory as well.
So this type of optimization is more like a coding optimization, more or less subjective. Not objective enough to be applied in a solid case.
Of course, if you want to fix a function instance for any third party function, ex. debounce. That might be a good use, but still smell fishy, because useMemo seems much more versatile to cover this case as well.
All in all, I'm only pointing out, useCallback isn't doing what's the public believe it can do, such as to bailout child component.

Resources