reactjs - 反应输入 onChange 滞后
问题描述
我有一个带有onChange
事件处理程序的简单受控输入。
一切正常,handleChange
每次击键时都会触发,问题是它非常慢。
使用输入时有一个非常明显的滞后。是否有一些额外的代码我必须正确才能让它像正常输入一样工作?
我必须对输入进行去抖动吗?
据我所知,文档中没有提到这个问题,我不知道我是否需要做一些额外的事情,或者我是否onChange
错误地使用了回调。
handleChange = (event) => {
this.setState({ itemNumber: event.target.value })
}
<TextField
id="Part #"
label="Part #"
value={this.state.itemNumber}
onChange={this.handleChange}
margin="normal"
/>
组件:
export class Dashboard extends Component {
state = {
report: '',
selectedDate: new Date(),
itemNumber: '',
}
static propTypes = {
classes: object,
headerTitle: string,
userInfo: object,
}
static defaultProps = {
classes: {},
headerTitle: undefined,
userInfo: {},
}
reportSelected = (event) => {
this.setState(() => {
return {
report: event.target.value,
}
})
}
handleDateChange = (date) => {
this.setState({ selectedDate: new Date(date) })
}
handleChange = (event) => {
this.setState({ itemNumber: event.target.value })
}
render () {
const { classes, headerTitle, userInfo } = this.props
return (
<div className={classes.dashboard}>
<HeaderTitle title="Dashboard" />
<Helmet>
<title>{headerTitle}</title>
</Helmet>
{ userInfo.isAuthorized &&
<Grid container direction={'row'} justify={'center'} className={classes.formContainer}>
<Grid item xs={12} sm={12} md={12} lg={6} xl={5}>
<form className={classes.form}>
<FormControl className={classes.presetReportsInput}>
<InputLabel htmlFor="reports">Preset Reports</InputLabel>
<Select
value={this.state.report}
onChange={this.reportSelected}
>
<MenuItem value="">
<em>None</em>
</MenuItem>
{presetReports.getReportList().map(report => (
<MenuItem value={report.name} key={report.name}>
{report.name}
</MenuItem>
))}
</Select>
</FormControl>
{ (this.state.report === 'Inventory Snapshot' ||
this.state.report === 'Weekly Fill Rate' ||
this.state.report === 'Open Orders' ||
this.state.report === 'Weekly Shipments') &&
<div>
<Grid container spacing={8} direction={'row'}>
<Grid item>
<MuiPickersUtilsProvider utils={MomentUtils}>
<DatePicker
className={classes.datePicker}
margin="normal"
keyboard
format="DD/MM/YYYY"
disableFuture
autoOk
mask={value => (value ? [/\d/, /\d/, '/', /\d/, /\d/, '/', /\d/, /\d/, /\d/, /\d/] : [])}
value={this.state.selectedDate}
onChange={this.handleDateChange}
disableOpenOnEnter
animateYearScrolling={false}
/>
</MuiPickersUtilsProvider>
</Grid>
<Grid item>
<TextField
id="Part #"
label="Part #"
value={this.state.itemNumber}
onChange={this.handleChange}
margin="normal"
/>
</Grid>
</Grid>
<Button variant="raised" color="primary" style={{ marginTop: 10 }}>
Search
</Button>
</div>
}
{ this.state.report === '' &&
<div>
<TextField
id="queryField"
label="Run a Query"
className={classes.queryField}
helperText=""
margin="normal"
multiline
rows="5"
/>
<Grid container direction={'row'} justify={'flex-end'}>
<Grid item>
<Button variant="raised" color="primary">
Export
</Button>
</Grid>
<Grid item>
<Button variant="raised" color="primary">
Save Query
</Button>
</Grid>
</Grid>
</div>
}
</form>
</Grid>
{ this.state.report === 'Inventory Snapshot' &&
<Grid container className={classes.table}>
<Grid item xs={12} sm={12} md={12} lg={12} xl={12}>
<InventoryReport />
</Grid>
</Grid>
}
</Grid>
}
</div>
)
}
}
const styles = {
dashboard: {},
formContainer: {
margin: 0,
width: '100%',
},
presetReportsInput: {
width: '100%',
margin: '20% 0 0 0',
},
queryField: {
width: '100%',
margin: '20% 0 0 0',
},
table: {
margin: '50px 0 10px 0',
},
datePicker: {
marginTop: 32,
},
}
const mapStateToProps = state => {
const { layout } = state
const { headerTitle } = layout
return {
headerTitle: headerTitle,
}
}
export default connect(mapStateToProps)(withStyles(styles)(Dashboard))
我正在查看 chrome 中 react devtools 中的状态更新,并且在输入字符和状态更新之间至少有 500 毫秒的延迟,对于更快的输入来说更长的时间。为什么 setState 这么慢?让这个表单表现得像一个普通的 web 表单的解决方法是什么?
解决方案
setState
它本身并不慢,只有当你的渲染变得非常昂贵时,它才会开始引起问题。
需要考虑的几点是:
- 您的主要组件似乎很大,并且在每次击键时都会重新渲染,因此可能会导致性能问题。尝试将其分解为更小的组件。
- 确保在
render
主组件的方法中渲染的子组件不会被不必要地重新渲染。React Developer Tools或Why-did-you-render可以指出那些不必要的重新渲染。切换到PureComponent
无状态组件或使用shouldComponentUpdate会有所帮助。 - 虽然您无法避免在这里重新渲染(因为您的表单输入需要使用新的状态值重新渲染),但通过分解成更小的组件,您可以使用shouldComponentUpdate让 React 知道组件的输出是否不受当前更改的影响状态或道具,并避免不必要的重新渲染。
- 如果您使用功能组件:
- 使用useMemo来防止重新计算昂贵的操作或组件,除非某些依赖项发生了变化。
- 使用useCallback返回回调的记忆版本,以便依赖引用相等的子组件不会不必要地重新渲染
- 如果您的功能组件在给定相同道具的情况下呈现相同的结果,请使用React.memo以防止它不必要地重新呈现。使用第二个参数来自
React.memo
定义 memoization 的行为(类似于shouldComponentUpdate
)
- 切换到生产版本以获得更好的性能
- 切换到不受控制的组件,让 DOM 自己处理输入组件(这是您描述的“正常 Web 表单”行为)。当您需要访问表单的值时,您可以使用
ref
's 来访问底层 DOM 节点并直接从中读取值。这应该消除调用setState
并因此重新渲染的需要
推荐阅读
- matlab - 如何在 matlab 中使用“保存”命令作为可索引命令
- python - 无法在python中写入文件
- android - 在 Firebase Phone Auth 中调用 GetIdTokenAsync() 会在 Xamarin 中引发 NullPointerException
- python - 熊猫单元测试
- excel - 从属下拉列表和偏移公式
- c - 从C中的函数返回数组地址的问题
- docker - 如何在 Tomee docker 容器上启用 https?
- jquery - 加载微调器 typeaheadjs 猎犬标签输入
- sql - 我的删除语句不起作用(使用 oracle 11g)
- android - 尝试加载图像时出现错误