【问题标题】:Using ReactDOM.render inside an event handler to avoid rendering expensive components在事件处理程序中使用 ReactDOM.render 以避免渲染昂贵的组件
【发布时间】:2016-01-07 23:32:24
【问题描述】:

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

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

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

这是我想出的解决方案:

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>
        );
    }
});

这似乎可行,但我想知道这种模式是否是执行此操作的“正确”方式。有没有更好的方法来动态呈现可能昂贵的 React 组件以响应事件?

编辑: 我找到了另一种看起来更“反应”的方法,但我不确定它是否完全正确。

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.

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

【问题讨论】:

    标签: javascript performance reactjs material-design


    【解决方案1】:

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

    让自己熟悉 state 的工作原理。我建议如下:

    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() 来检测日期选择器何时被激活。

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

    【讨论】:

    • 我找到了与您发布的相同的解决方案。谢谢!但是,我仍然在控制台中收到警告。请看我上面的编辑。
    • 当您将 showDatePicker 设置为 false 时,您将从 DOM 中删除 DatePicker。我的猜测是在你这样做的同时,DatePicker 正试图改变自己的状态以关闭对话框。因此,有关更改未安装组件状态的警告。我再次重申我在上面最后一段中提出的问题。真的有必要复制已经内置在 DatePicker 组件中的显示/隐藏行为吗?
    • 通过一些非正式测试,如果我在每个项目中都包含日期选择器,则在呈现 25 个项目的列表时,它似乎会慢约 30%。查看组件here 的来源,似乎日期选择器呈现了相当多的组件,这些组件直到用户交互才显示。我不认为这会是一个问题,除非在像我这样的情况下,我想在任意大小的列表的每个元素中都有一个日期选择器。
    猜你喜欢
    • 2018-05-16
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2018-07-01
    • 2017-02-03
    • 1970-01-01
    • 2021-07-10
    相关资源
    最近更新 更多