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