My intention is to create a custom input element that avoids re-rendering whenever the input value changes, but at the same time notifies the parent if the input changes.
Input component:
import { useRef, useEffect, ChangeEvent } from "react";
type props = {
changeFunction?: (e: ChangeEvent<HTMLInputElement>) => void;
};
function TestInput({ changeFunction }: props) {
let inputRef = useRef<HTMLInputElement>(null);
useEffect(() => {
console.log("Input render");
});
function change(e: ChangeEvent<HTMLInputElement>) {
console.log("Input change", e.target.value);
if (inputRef.current) inputRef.current.value = e.target.value;
if (changeFunction) changeFunction(e);
}
return (
<input
ref={inputRef}
type="search"
id="test"
name="test"
autoComplete="off"
onChange={change}
/>
);
}
export default TestInput;
Parent component:
import { useCallback, useState, ChangeEvent } from "react";
import TestInput from "./Test";
function TestParent() {
let [value, setValue] = useState("");
let change = useCallback(
(e: ChangeEvent<HTMLInputElement>) => setValue(e.target.value),
[]
);
return (
<div>
<p>Value: {value}</p>
<TestInput changeFunction={change} />
</div>
);
}
export default TestParent;
Despite using useCallback
the input component re-renders every time. If we exclude the changeFunction
prop this doesn’t happen. What could be the solution?
Here’s a Stack Snippet without the TypeScript parts showing the problem.
const { useState, useEffect, useCallback, useRef } = React;
function TestInput({ changeFunction }) {
let inputRef = useRef(null);
useEffect(() => {
console.log("Input render");
});
function change(e) {
console.log("Input change", e.target.value);
if (inputRef.current) inputRef.current.value = e.target.value;
if (changeFunction) changeFunction(e);
}
return (
<input
ref={inputRef}
type="search"
id="test"
name="test"
autoComplete="off"
onChange={change}
/>
);
}
function TestParent() {
let [value, setValue] = useState("");
let change = useCallback(
(e) => setValue(e.target.value),
[]
);
return (
<div>
<p>Value: {value}</p>
<TestInput changeFunction={change} />
</div>
);
}
const root = ReactDOM.createRoot(document.getElementById("root"));
root.render(<TestParent />);
<div id="root"></div>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/18.1.0/umd/react.development.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/18.1.0/umd/react-dom.development.js"></script>
2
Answers
You could use
React.memo
. Because input props are not changing the input will not be re-rendered when its parent state changes.Rerendering of the child component is not something you need to avoid. That is how react works out the changes necessary to be made to the dom. So unless your use case specifically requires the child component to not rerender you should avoid these kinds of perfomance optimizations, that said.
You could Memoize the child component