问题
I have a React app with a list of tasks which each have due dates. I want the user to be able to click the due date to pop up a datepicker to select a new one.
I am using the material UI library http://www.material-ui.com/#/components/date-picker
I could simply render a datepicker on each task in the list but it seems like it might cause performance problems on a long list to render such a complex component on each item. I would prefer to just display the due date and only render the datepicker when the user clicks to change the due date.
Here is the solution I have come up with:
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>
);
}
});
This seems to work, but I want to know if this pattern is the "correct" way to do this. Is there a better way to dynamically render potentially expensive react components in response to an event?
Edit: I have found another way to do this which seems more "react" but I am not sure if it is entirely correct.
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>
);
}
});
This correctly shows and hides the datepicker, but when setting the closed state I get the following warning in my console:
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.
Despite this warning it seem the state is correctly set on my component. Should I ignore this warning? Is my new pattern correct?
回答1:
Only use ReactDOM.render() when mounting your React app. It should never be used within a React component itself.
Familiarise yourself with how state works. I suggest the following:
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>
);
}
});
This achieves the same thing in a cleaner fashion. The state variable displayDatePicker determines whether or not the datepicker shows. I've also used componentDidUpdate() to detect when the datepicker has just been activated.
One further thing however. I think you should question your assumption that displaying the datepicker by default would necessarily create performance problems. Do you know that to be the case? I fear you might be trading an increase in code complexity for a trivial performance boost.
来源:https://stackoverflow.com/questions/34667096/using-reactdom-render-inside-an-event-handler-to-avoid-rendering-expensive-compo