I am trying to understand how to stop rerendering elements in functional component (or maybe I don't need to ?)
I am using single useState for all inputs and share the same onChange handler.
I have huge amounts of input fields in forms (100+) and whenever I type in any of them in dev tools profiler I see they all rerender, assuming this is because on each renders my onChange handler is "recreated" and all input elements rerender too. If I try to wrap it in useCallback nothing changes, but if I wrap my input element in Rect.memo component then it doesn't rerender.
const MyInput = props => {
return <input {...props} />;
};
const MyInputMemo = React.memo(props => {
return <input {...props} />;
});
function App() {
React.useDebugValue("test");
const [formValues, setFormValues] = useState({
name1: "test 1",
name2: "test 2",
name3: "test 3"
});
const [singleInput, setSingleInput] = useState("single input handler");
const onHandleInputChange = e => {
setFormValues({ ...formValues, [e.target.name]: e.target.value });
};
const onHandleInputChangeSingle = useCallback(e => {
setSingleInput(e.target.value);
}, []);
return (
<div className="App" style={{ padding: "30px" }}>
<div>
<h1>Test form</h1>
</div>
<form>
<div>
<label>shared onChange</label>
<MyInput
name="name1"
value={formValues.name1}
onChange={onHandleInputChange}
/>
</div>
<div>
<label>shared onChange</label>
<MyInput
name="name2"
value={formValues.name2}
onChange={onHandleInputChange}
/>
</div>
<div>
<label>shared onChange (memo)</label>
<MyInputMemo
name="name3"
value={formValues.name3}
onChange={onHandleInputChange}
/>
</div>
<div>
<label>self onChanged</label>
<input
name="name4"
value={singleInput}
onChange={onHandleInputChangeSingle}
/>
</div>
<div>
<pre>shared state:{JSON.stringify(formValues, null, 2)}</pre>
</div>
<div>
<pre>single state: {JSON.stringify(singleInput, null, 2)}</pre>
</div>
</form>
</div>
);
}
here is simplified sandbox example
Am I profiling something wrong or is this normal behavior, should I be concerned about performance and if so how to fix it?
By default React recalculates all the variables inside of functional component.
You have to use useCallback
and useMemo
to memoize variables and recalculate them only if their dependencies change.
So you have to wrap onHandleInputChange
in useCallback
(otherwise you always recreate it and this prop updates for inputs every render).
Additionally, you have to wrap your parent and children functional components in React.memo
. It's equivalent of PureComponent
for classes.