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

时间:2016-01-07 23:32:25

标签: javascript performance reactjs material-design

我有一个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>
        );
    }
});

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

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

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.

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

1 个答案:

答案 0 :(得分:2)

在安装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()来检测刚刚启动了日期选择器的时间。

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