Reputation: 526
When initializing state in React, is it bad practice to define functions inside of useState
rather than using useEffect
? Or is it best to use useMemo
?
My understanding is that variables inside useState
will be set on the first render of the component. Are there any additional considerations when using functions to set the initial state?
import React, { useState, useEffect } from "react";
export default function Token() {
const a = 5
const b = 5
const findToken = (a,b) => {
return a*b;
};
const [token, setToken] = useState(() => {
return findToken(a,b);
});
return (
<div>
<h1>Token: {token}</h1>
</div>
);
}
import React, { useState, useEffect } from "react";
export default function Token() {
const a = 5
const b = 5
const [token, setToken] = useState();
useEffect(() => {
setToken(a*b);
}, []);
return (
<div>
<h1>Token: {token}</h1>
</div>
);
}
import React, { useState, useEffect } from "react";
export default function Token() {
const a = 5
const b = 5
const findToken = (a,b) => {
return a*b;
};
const [token, setToken] = useState(useMemo(() => findToken(a,b), [a,b]));
return (
<div>
<h1>Token: {token}</h1>
</div>
);
}
Upvotes: 1
Views: 764
Reputation: 28654
Below I will try to compare some of the approaches you listed for setting initial state.
useState
with initializer function
const [token, setToken] = useState(() => {
return findToken(a,b);
});
You use this approach when findToken
is relatively expensive, and using this approach it will be guaranteed that findToken
will be executed only once. From the docs:
If the initial state is the result of an expensive computation, you may provide a function instead, which will be executed only on the initial render
Because if you passed an expression instead of function to useState
, that expression would be run on each render, just its value gets ignored after first render.
Approach with useEffect
useEffect(() => {
setToken(a*b);
}, []);
In terms of functionality this is similar to approach with useState
above, but this is unnecessary and causes an additional re render.
Also there is another difference, the initializer function passed to useState
should be pure, whereas you don't have that requirement with useEffect
, so in the approach with useEffect
you have advantage that you can set value in state which is result of say an API call.
Approach with useMemo
const [token, setToken] = useState(useMemo(() => findToken(a,b), [a,b]));
Above approach doesn't make sense actually because what will happen is first time useMemo
runs, its result will be stored in useState
. Then, all other values useMemo
will generate will be actually ignored - as already mentioned in the first section about passing expressions to useState
.
Upvotes: 3
Reputation: 370689
If the value will only be set exactly once, and the operation that needs to be performed isn't expensive, then it would make the most sense to avoid state and use only useMemo
. For example, code like this:
const [token, setToken] = useState(() => {
return findToken(a,b);
});
if setToken
isn't called elsewhere and the calculation isn't expensive, would always be better refactored to
const token = useMemo(() => findToken(a, b), []);
If the value could be set multiple times depending on logic in your app, state will be needed.
If the operation that calculates the value is expensive enough to cause noticeable delays when rendering, then you might decide to compensate for that by using state, initially render an empty value, and then use useEffect
to calculate (and set the state) for the expensive value after the component has been painted to the screen.
If you set state in an effect hook (as opposed to useMemo
), keep in mind that that'll cause re-rendering of components lower in the tree, which is sometimes undesirable.
Upvotes: 2