Home > Enterprise >  What is best practice for using a function multiple times in a react component?
What is best practice for using a function multiple times in a react component?

Time:09-05

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>
  );
}
  • Related