在事件处理程序中使用ReactDOM.render来避免渲染昂贵的组件

Using ReactDOM.render inside an event handler to avoid rendering expensive components

本文关键字:组件 程序 事件处理 render ReactDOM      更新时间:2023-09-26

我有一个React应用程序,它有一个任务列表,每个任务都有截止日期。我希望用户能够点击截止日期,弹出一个日期选择器来选择一个新的日期。

我正在使用材质UI库http://www.material-ui.com/#/components/date-拾取器

我可以简单地为列表中的每个任务呈现一个日期选择器,但在长列表中为每个项目呈现如此复杂的组件似乎可能会导致性能问题。我更喜欢只显示截止日期,并且只在用户单击更改截止日期时呈现日期选择器。

以下是我提出的解决方案:

TaskFooter = React.createClass({
    propTypes: {
        task: React.PropTypes.object.isRequired
    },
    onDateChanged(e, newDate){
        // handle data update
    },
    showDatePicker(){
        let dp = ReactDOM.render(
            <MUI.DatePicker autoOk={true} textFieldStyle={{display: 'none'}} onChange={this.onDateChanged} />,
            this.refs.datePickerContainer
        );
        dp.openDialog();
    },
    render() {
        return (
                <div onClick={this.showDatePicker}>
                    <MUI.Libs.SvgIcons.ActionEvent />
                    {this.props.task.duedate}
                    <div ref="datePickerContainer"></div>
                </div>
        );
    }
});

这似乎有效,但我想知道这种模式是否是"正确"的方法。有没有更好的方法来动态呈现可能昂贵的反应组件以响应事件?

编辑:我找到了另一种方法来做到这一点,它似乎更"反应",但我不确定它是否完全正确。

TaskFooter = React.createClass({
    propTypes: {
        task: React.PropTypes.object.isRequired
    },
    getInitialState(){
        return {
            showDatePicker: false
        }
    },
    componentDidUpdate(prevProps, prevState){
        if(this.state.showDatePicker && !prevState.showDatePicker){
            this.refs.datePicker.openDialog();
        }
    },
    onDateChanged(e, newDate){
        console.log('new date', newDate);
        this.setState({
            showDatePicker: false
        });
    },
    showDatePicker(){
        this.setState({
            showDatePicker: true
        });
    },
    render() {
        return (
                <div onClick={this.showDatePicker}>
                    <MUI.Libs.SvgIcons.ActionEvent />
                    {this.props.task.duedate}
                    { this.state.showDatePicker ?
                        <MUI.DatePicker autoOk={true} textFieldStyle={{display: 'none'}} onChange={this.onDateChanged} ref="datePicker" />
                    : ''}
                </div>
        );
    }
});

这正确地显示和隐藏了日期选择器,但当设置关闭状态时,我在控制台中收到以下警告:

Warning: setState(...): Can only update a mounted or mounting component. This usually means you called setState() on an unmounted component. This is a no-op. Please check the code for the DatePickerDialog component.

尽管有此警告,但我的组件上的状态似乎设置正确。我应该忽略这个警告吗?我的新模式正确吗?

安装React应用程序时仅使用ReactDOM.render()。它不应该在React组件本身中使用。

熟悉状态的工作原理。我建议如下:

TaskFooter = React.createClass({
    propTypes: {
        task: React.PropTypes.object.isRequired
    },
    getInitialState() {
        return {
            displayDatePicker: false
        };
    }
    onDateChanged(e, newDate) {
        // handle data update
    },
    showDatePicker() {
        this.setState({
            displayDatePicker: false
        });
    }
    componentDidUpdate(prevProps, prevState) {
        // this is a little bit hacky,
        // ideally there would be a prop for DatePicker to instruct it to already be open
        if (this.state.displayDatePicker && !prevState.displayDatePicker) {
            this.refs.dp.openDialog();
        }
    },
    render() {
        return (
            <div onClick={this.showDatePicker}>
                <MUI.Libs.SvgIcons.ActionEvent />
                {this.props.task.duedate}
                {this.state.displayDatePicker ? (
                    <MUI.DatePicker
                        ref="dp"
                        autoOk={true}
                        textFieldStyle={{display: 'none'}}
                        onChange={this.onDateChanged}
                    />
                ) : null}
            </div>
        );
    }
});

这以更清洁的方式实现了同样的目的。状态变量displayDatePicker确定日期选择器是否显示。我还使用了componentDidUpdate()来检测日期选择器何时刚刚被激活。

然而,还有一件事。我认为您应该质疑您的假设,即默认情况下显示日期选择器必然会产生性能问题。你知道是这样吗?我担心您可能会用代码复杂性的增加来换取微不足道的性能提升。