I'm using React 17 and MUI 5. I found a problem in my project when I have a component with a huge form with multiple <Textfields>
.
handleChange = (event) => {
this.setState({ value: event.target.value });
};
...
<TextField
value={this.state.value}
onChange={this.handleChange}
label="Textfield"
/>
Everytime I type a char in my textfield the setState is triggered and my render()
function is called everytime. So on my huge form I have a lag when I type.
So I tried the solution to use onBlur
event:
<TextField
onBlur={this.handleChange}
label="Textfield"
/>
It works well if I'm on "Add" mode because the setState is triggered only when I leave the textfield. But I have a problem when I'm on "Edit" mode. When I'm on "Edit" mode I get the current value and I put it inside my textfield. And with onBlur
event I can't use the textfield value
prop because there is a conflict and I can't write anymore in the textfield.
So I found another solution. It's to use ref
. But I have the same problem like with onBlur
event. I can't use value
prop. So I did this :
<TextField
inputRef={(el) => {
this.textRef = el;
if (el && this.state.mode === "edit") {
this.textRef.value = this.state.node.value;
}
}}
label="Textfield"
/>
So when I'm on edit mode I init the textfield with the current value (this.state.node is a node of my TreeView and each node have a edit button). This code works. But I have the feeling it's not the good way to do that. This if mode == edit
is weird.
Do you have a better way ?
CodePudding user response:
You could examine uncontrolled inputs, it could fit in your case
An uncontrolled form doesn’t have state, so every time you type, your component doesn’t re-render, increasing overall performance.
There is an explanation: https://hackernoon.com/you-might-not-need-controlled-components-fy1g360o
CodePudding user response:
I doubt the "huge form" is actually the problem. React only updates the DOM when there are changes, so even though the component is rerendered on every character typed, only the one input field that changes should get flushed to the DOM.
Check if you're doing expensive calculations in your render()
function. In that case, memoize these.
Another take on fixing this would be to use uncontrolled components as mentioned in the other answer.
Also consider if you can split up your component into multiple, smaller components so the problem is minimized.
The following "benchmark" code is written with functional components but is using MUI v5 and React 17 and runs with 100 TextField
s without any noticable lag when entering text, at least on my (relatively beefy) desktop PC.
Using <input>
from HTML even allowed me to get this working with 1000 elements.
const inputFieldKeys = Array.from({
length: 100
}).map((e, i) => "field_" i.toString());
const App = (props) => {
const [inputState, setInputState] = React.useState({});
// this runs fine even without the `useCallback` optimization, at least for me
const handleChange = React.useCallback((e) => {
setInputState(oldState => ({ ...oldState,
[e.target.name]: e.target.value
}));
}, [setInputState]);
return ( <div>
{inputFieldKeys.map(key=>(
<MaterialUI.TextField name={key} key={key} value={inputState[key] || ""} onChange={handleChange}
label={key}/>
))}
</div>
);
};
ReactDOM.render( < App / > , document.getElementById("root"));
<script crossorigin src="https://unpkg.com/react@17/umd/react.development.js"></script>
<script crossorigin src="https://unpkg.com/react-dom@17/umd/react-dom.development.js"></script>
<script crossorigin src="https://unpkg.com/@mui/material@5/umd/material-ui.production.min.js"></script>
<div id="root"></div>
Note for MUI performance:
MUI v5 uses the sx
prop for inline styling. This can lead to bad performance in cases with many components when used without care. mui docs