I have component with some calculation functions and im looking for best practice for using my functions multiple times in other functions and conditions; without breaking rules or overusing useEffect
and useState
(or should i?).
export default function App() {
const [data, setData] = React.useState({});
const [render, setRender] = React.useState(0);
const calculate1 = () => {
if (data.object?.number > 100) return 256
return 0
}
const calculate2 = () => {
if (calculate1() > 0) return 0
return 1
}
React.useEffect(() =>{
axios.get('api').then((response) =>
setData(response.data))
},[])
return (
<div>
{!!calculate1() && <h1>{calculate1()}</h1>}
{!!calculate2() && <h2>{calculate1() 1}</h2>}
<button onClick={() => setRender(render 1)}>render</button>
</div>
);
}
1- Should i save calculated in a variable then use it anywhere? if yes, how to prevent calling calculations on each render with render button?
const result1 = calculate1()
const result2 = calculate2()
2- Should i use useMemo
to keep calculations isolated? (calculations are not expensive)
const result1 = React.useMemo(() => {
if (data.object?.number > 100) return 256
return 0
}, [data])
const result2 = React.useMemo(() => {
if (result1 > 0) return 0
return 1
},[result1])
3- Other solutions?
CodePudding user response:
Saving the result of the calculation like in 1 won't work because on each render the calculate1
and calculate2
runs again (you rightly identified that on a rerender it runs again).
Your example in 2 is correct if you want to memoise the values. Generally though for non-expensive calculations you should skip useMemo
. There's overhead from using useMemo
and it does make your code slightly more complex to read. You should only consider useMemo
if the calculation is computationally heavy (you should decide for yourself).
But for your example I would just suggest:
const calculate1Value = data.object?.number > 100 ? 256 : 0
const calculate2Value = calculate1Value > 0 ? 0 : 1
// ...
CodePudding user response:
I'm assuming calculate()
was supposed to be calculate1()
– in that case, I'd just do
export default function App() {
const [data, setData] = React.useState({});
const [render, setRender] = React.useState(0);
React.useEffect(() => {
axios.get('api').then(({data}) => setData(data));
}, []);
const result1 = (data.object?.number > 100 ? 256 : 0);
const result2 = (result1 > 0 ? 0 : 1);
return (
<div>
{!!result1 && <h1>{result1}</h1>}
{!!result2 && <h2>{result1 1}</h2>}
<button onClick={() => setRender(render 1)}>render</button>
</div>
);
}
and in case the actual computation was heavier than that and worth caching,
export default function App() {
const [data, setData] = React.useState({});
const [render, setRender] = React.useState(0);
React.useEffect(() => {
axios.get('api').then(({data}) => setData(data));
}, []);
const [result1, result2] = React.useMemo(() => {
const result1 = (data.object?.number > 100 ? 256 : 0);
const result2 = (result1 > 0 ? 0 : 1);
return [result1, result2];
}, [data]);
return (
<div>
{!!result1 && <h1>{result1}</h1>}
{!!result2 && <h2>{result1 1}</h2>}
<button onClick={() => setRender(render 1)}>render</button>
</div>
);
}
Finally, if the same computation is required in multiple components, I would wrap it in a hook:
function useResults(data) {
const [result1, result2] = React.useMemo(() => {
const result1 = (data.object?.number > 100 ? 256 : 0);
const result2 = (result1 > 0 ? 0 : 1);
return [result1, result2];
}, [data]);
return {result1, result2};
}
export default function App() {
const [data, setData] = React.useState({});
const [render, setRender] = React.useState(0);
React.useEffect(() => {
axios.get('api').then(({data}) => setData(data));
}, []);
const {result1, result2} = useResults(data);
return (
<div>
{!!result1 && <h1>{result1}</h1>}
{!!result2 && <h2>{result1 1}</h2>}
<button onClick={() => setRender(render 1)}>render</button>
</div>
);
}
and if it was always paired with the same data fetch,
function useDataAndResults() {
const [data, setData] = React.useState({});
React.useEffect(() => {
axios.get('api').then(({data}) => setData(data));
}, []);
const [result1, result2] = React.useMemo(() => {
const result1 = (data.object?.number > 100 ? 256 : 0);
const result2 = (result1 > 0 ? 0 : 1);
return [result1, result2];
}, [data]);
return {data, result1, result2};
}
export default function App() {
const [render, setRender] = React.useState(0);
const {data, result1, result2} = useDataAndResults();
return (
<div>
{!!result1 && <h1>{result1}</h1>}
{!!result2 && <h2>{result1 1}</h2>}
<button onClick={() => setRender(render 1)}>render</button>
</div>
);
}