Home > front end >  Avoid Inline Function Definition in the Render Function
Avoid Inline Function Definition in the Render Function

Time:12-25

I saw many article said that define inline function in render function in react can cause to performance issue.
Therefore they recommend to define the function outside of the render function and use it where i need (onClick etc).
I built a sample code that i have a list of button and each button will increase the state by the index in the list, But its throw error.
How i can pass parameter and not use inline function in onClick

const App = () => {
  const [number, setNumber] = useState(1);
  const increaseNumber = (num) => {
    setNumber((prevState) => prevState   num);
  };
  return (
    <div>
      {[...Array(5)].map((item, index) => (
        <button key={index} onClick={increaseNumber(index)}>
          {`increase by ${index}`}
        </button>
      ))}
      <div>{number}</div>
    </div>
  );
};
export default App;

CodePudding user response:

I'll preface my answer by saying you really should profile your application and identify specific performance issues before trying to optimize anything. In this case, you could avoid creating a new callback with each map iteration by using data attributes.

function App() {
    const [number, setNumber] = useState(1);

    const increaseNumber = (event) => {
        const index = parseInt(event.target.dataset.index);
        setNumber((prevState) => prevState   index);
    };

    return (
        <div>
            {[...Array(5)].map((item, index) => (
                <button key={index} onClick={increaseNumber} data-index={index}>
                    {`increase by ${index}`}
                </button>
            ))}
            <div>{number}</div>
        </div>
    );
}

export default App;

Typically the only time you would care about creating a new callback per render is when the callback is used as a prop in a child component. Using something like useCallback can help to avoid unnecessary child renders in those cases.

CodePudding user response:

That's a good question, as it's a common scenario usually ignored in tutorials. You would normally employ useCallback, but how to do here, when you have builder arguments?

Aproach 1: useMemo

When the arguments are fixed like it's your case, you may use useMemo:

import { useMemo, useState } from "react";

const indexes = [...Array(5)].map((_item, idx) => idx);

const App = () => {
    const [number, setNumber] = useState(1);

    const increaseNumber = useMemo(() => {
        return indexes.map(index => () => setNumber(prevNumber => prevNumber   index));
    }, [indexes]);

    return (
        <div>
            {indexes.map(index => (
                <button key={index} onClick={increaseNumber[index]}>
                    increase by {index}
                </button>
            ))}

            <div>{number}</div>
        </div>
    );
};

Approach 2: wraper component useCallback

Create your wrapper component Button:

const IncreaseButton = ({ setNumber, index }) => {
    const increaseByIndex = useCallback(() => {
        return setNumber(prevValue => prevValue   index);
    }, [setNumber, index]);

    return <button onClick={increaseByIndex}>increase by {index}</button>;
};

CodePudding user response:

You can pass an item as a function that had been memoized to the onClick prop of react button elements.

const App = () => {
  const [number, setNumber] = useState(1);
  const increaseNumber = (num) => () => {
    setNumber((prevState) => prevState   num);
  };

  const btns = useMemo(() => {
    // here I am using lodash memoize function you may use your own
    let inc = _.memoize(increaseNumber)
    return Array(500).fill(0).map((_, index) => inc(index))
  }, [])
  return (
    <div>
      {btns.map((item, index) => (
        <button key={index} onClick={item}>
          {`increase by ${index}`}
        </button>
      ))}
      <div>{number}</div>
    </div>
  );
};
  • Related