MUI TextField如何用useMemo/useCallback优化onChange性能
关键误区先澄清
useCallback和useMemo是React函数组件专属的Hooks接口,类组件里根本用不了。你现在写的是类组件,硬套这两个API会直接抛错,先搞清楚这个前提再谈优化。
类组件场景下的正确优化(不需要改函数组件)
你现在代码里的handleChange用了类属性箭头函数写法,本身在组件全生命周期里引用都是固定的,根本不会因为函数引用变化触发多余重渲染,这部分写的没问题。
你遇到的输入卡顿90%概率是两个低级问题导致的:
- 每次render都给
InputLabelProps传了新创建的对象,MUI v4的TextField内部会做props浅比较,新对象引用会触发整个组件树全量重渲染 - 父组件输入setState时,连带渲染了很多和输入框无关的重型组件,拖慢了渲染速度
对应优化代码:
// 固定不变的配置项直接抽到组件外面,不要放在render里每次新建 const FIXED_INPUT_LABEL_OPTIONS = { shrink: true }; class MyComponent extends Component { state = { city: "", }; handleChange = (event) => { const { name, value } = event.target; this.setState({ [name]: value }); }; render() { return ( <TextField label="City" name="city" value={this.state.city} onChange={this.handleChange} InputLabelProps={FIXED_INPUT_LABEL_OPTIONS} /> {/* 其他重型组件尽量做拆分,避免输入重渲染时连带更新 */} ) } }
如果类组件里无关重渲染还是很多,可以把Component换成PureComponent,它会自动对state和props做浅比较,跳过不必要的重渲染。
如果你要重构为函数组件,useCallback/useMemo用法如下
如果后续打算把类组件迁到函数组件,两个API的用法非常简单:
useCallback用来缓存函数引用,避免每次render新建函数传给子组件触发多余更新useMemo用来缓存对象、计算结果这类引用类型值,同样是为了避免无意义的引用变化
优化后的函数组件代码:
import { useState, useCallback, useMemo } from 'react'; import TextField from '@material-ui/core/TextField'; const MyComponent = () => { const [city, setCity] = useState(''); // 缓存onChange处理函数,空依赖表示函数永远不更新 const handleCityChange = useCallback((e) => { setCity(e.target.value); }, []); // 缓存传给InputLabelProps的对象,空依赖表示对象永远不更新 const inputLabelProps = useMemo(() => ({ shrink: true }), []); return ( <TextField label="City" name="city" value={city} onChange={handleCityChange} InputLabelProps={inputLabelProps} /> ); }; export default MyComponent;
额外提醒
不要上来就给所有函数、所有值套useCallback/useMemo,这两个API本身有内存和计算开销,只有当你把值传给会做浅比较的子组件(比如MUI组件、React.memo包裹的自定义组件)时,缓存引用才有性能收益,滥用反而会变慢。
如果优化后还是有明显卡顿,可以给输入逻辑加100ms以内的轻量防抖,避免快速输入时频繁触发setState。
内容的提问来源于stack exchange,提问作者John
相关产品推荐
相关产品推荐

