如何根据 属性 正确处理订阅
How to properly handle subscriptions depending on property
我有一个全局服务 widgetService
,它包含许多小部件的数据,每个小部件由 widgetID
标识。每个小部件的数据都可以随时更改。我想显示一个带有 React 组件的小部件,比如 WidgetReactComponent
。
react 组件将widget ID 作为属性,并从widget 服务获取要显示的信息。可以使用方法 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
returns 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);
}
关于您的疑虑:
componentDidUpdate won't be called when shouldComponentUpdate returns false
来自docs:
Use shouldComponentUpdate() to let React know if a component’s output is not affected by the current change in state or props.
因此,如果您决定在 this.props.widgetID
更改时不更新组件,那么您就违反了 shouldComponentUpdate
的 assumption/purpose,并且不应期望更新您的小部件侦听器。
如果您滥用 shouldComponentUpdate
,很多事情将无法按预期工作(例如,组件未更新以反映新数据),因此依赖 API 根据官方正确使用文档是实现简单性的必要条件,而不是要避免的东西。
the listeners will be wrong for the entire timespan between the property change and the update's completion
按照这个逻辑,当您在事件处理程序中更新一些显示的数据时,您也可以声称在事件和重新呈现之间的整个时间跨度内显示的数据都是错误的。你甚至可以声称你的文本编辑器在你按下键盘键和在屏幕上呈现该键之间显示了错误的数据。
我有一个全局服务 widgetService
,它包含许多小部件的数据,每个小部件由 widgetID
标识。每个小部件的数据都可以随时更改。我想显示一个带有 React 组件的小部件,比如 WidgetReactComponent
。
react 组件将widget ID 作为属性,并从widget 服务获取要显示的信息。可以使用方法 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
returns 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);
}
关于您的疑虑:
componentDidUpdate won't be called when shouldComponentUpdate returns false
来自docs:
Use shouldComponentUpdate() to let React know if a component’s output is not affected by the current change in state or props.
因此,如果您决定在 this.props.widgetID
更改时不更新组件,那么您就违反了 shouldComponentUpdate
的 assumption/purpose,并且不应期望更新您的小部件侦听器。
如果您滥用 shouldComponentUpdate
,很多事情将无法按预期工作(例如,组件未更新以反映新数据),因此依赖 API 根据官方正确使用文档是实现简单性的必要条件,而不是要避免的东西。
the listeners will be wrong for the entire timespan between the property change and the update's completion
按照这个逻辑,当您在事件处理程序中更新一些显示的数据时,您也可以声称在事件和重新呈现之间的整个时间跨度内显示的数据都是错误的。你甚至可以声称你的文本编辑器在你按下键盘键和在屏幕上呈现该键之间显示了错误的数据。