Home > OS >  Improve textfield render performance
Improve textfield render performance

Time:07-08

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 TextFields 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

  • Related