我有一个全局服务widgetService
,其中包含许多小部件的数据,每个小部件都由widgetID
标识。每个小部件的数据都可以随时更改。我想显示一个带有React组件的小部件,例如WidgetReactComponent
。
react组件应将小部件ID作为属性,并从小部件服务中获取要显示的信息。可以使用getWidgetData(widgetID)
方法从小部件服务中查询小部件的数据。为了能够发布数据更改,它还提供了两种方法:addListenerForWidget(widgetID, listener)
和removeListenerForWidget(widgetID, listener)
。
当假设该属性设置一次且从未更改时,可以按照React的建议通过以下方式实现:
class WidgetReactComponent extends Component {
constructor() {
super();
this.state = {
data: widgetService.getWidgetData(this.props.widgetID)
};
this._onDataChange = this._onDataChange.bind(this);
}
_onDataChange(newData) {
this.setState({data: newData});
}
componentDidMount() {
// React documentation: "This method is a good place to set up any subscriptions."
widgetService.addListenerForWidget(this.props.widgetID, this._onDataChange);
}
componentWillUnmount() {
// React documentation: "Perform any necessary cleanup in this method, such as [...] cleaning up any subscriptions that were created in componentDidMount()."
widgetService.removeListenerForWidget(this.props.widgetID, this._onDataChange);
}
render() {
return <div className="Widget">{this.state.data.stuff}</div>;
}
}
然后可以像下面这样使用该组件:
<ReactWidgetComponent widgetID={17} />
但是,
widgetID
属性可以随时更改,并且组件必须对其进行处理才能在所有情况下都能正常运行。根据react的建议,应该通过使用static getDerivedStateFromProps
函数基于属性设置状态来处理此问题。但是由于它是静态的,所以我无法访问该组件,因此无法相应地更改监听器。解决此问题的一种方法是将
widgetID
存储在状态中,然后使用生命周期方法componentDidUpdate
来检测更改,如下所示:constructor() {
super();
this._onDataChange = this._onDataChange.bind(this);
}
static getDerivedStateFromProps(nextProps) {
return {
widgetID: nextProps.widgetID,
data: widgetService.getWidgetData(nextProps.widgetID)
};
}
componentDidUpdate(prevProps, prevState) {
if (prevState.widgetID !== this.state.widgetID) {
widgetService.removeListenerForWidget(prevState.widgetID, this._onDataChange);
widgetService.addListenerForWidget(this.state.widgetID, this._onDataChange);
}
}
但是,当
componentDidUpdate
返回shouldComponentUpdate
时,不会调用false
。这样做感觉不安全。我也相信,在属性更改和更新完成之间的整个时间范围内,监听器都是错误的。我怎样才能安全地实现这一目标? 最佳答案
您不需要以状态存储widgetID
,可以将prevProps
与this.props
进行比较:
componentDidUpdate(prevProps, prevState) {
if (prevProps.widgetID !== this.props.widgetID) {
widgetService.removeListenerForWidget(prevProps.widgetID, this._onDataChange);
widgetService.addListenerForWidget(this.props.widgetID, this._onDataChange);
}
}
您还需要在
componentDidMount
中添加监听器,因为在第一次渲染时未调用componentDidUpdate
:componentDidMount() {
widgetService.addListenerForWidget(this.props.widgetID, this._onDataChange);
}
关于您的担忧:
从docs:
因此,如果您决定在
this.props.widgetID
更改时不更新组件,那么您就违反了shouldComponentUpdate
的假设/用途,并且不应期望小部件监听器会被更新。如果您仍然滥用
shouldComponentUpdate
,很多事情将无法按预期进行(例如,组件未更新以反射(reflect)新数据),因此,必须根据官方文档正确使用API,以实现简便性,而不是简单地做到这一点。避免。通过这种逻辑,当您在事件处理程序中更新某些显示的数据时,您还可以声明在事件和重新渲染之间的整个时间段内显示的数据都是错误的。您甚至可以声称您的文本编辑器在您按下键盘键和在屏幕上呈现键之间显示了错误的数据。