如何根据属性正确处理订阅

Fel*_*elk 7 javascript listener publish-subscribe reactjs react-props

我有一个全局服务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>;
    }
}
Run Code Online (Sandbox Code Playgroud)

然后可以像这样使用该组件:

<ReactWidgetComponent widgetID={17} />
Run Code Online (Sandbox Code Playgroud)

但是,该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);
    }
}
Run Code Online (Sandbox Code Playgroud)

但是,返回componentDidUpdate时不会被调用.这不是一种安全的方式.此外,我认为听众在属性更改和更新完成之间的整个时间跨度都是错误的.我怎么能安全地实现这个呢?shouldComponentUpdatefalse

riw*_*iwu 1

您不需要存储widgetID在状态中,您可以prevProps与以下内容进行比较this.props

\n\n
componentDidUpdate(prevProps, prevState) {\n  if (prevProps.widgetID !== this.props.widgetID) {\n    widgetService.removeListenerForWidget(prevProps.widgetID, this._onDataChange);\n    widgetService.addListenerForWidget(this.props.widgetID, this._onDataChange);\n  }\n}\n
Run Code Online (Sandbox Code Playgroud)\n\n

您还需要添加侦听器,componentDidMount因为componentDidUpdate在第一次渲染时不会调用:

\n\n
componentDidMount() {\n  widgetService.addListenerForWidget(this.props.widgetID, this._onDataChange);\n}\n
Run Code Online (Sandbox Code Playgroud)\n\n

关于您的担忧:

\n\n
\n

当 shouldComponentUpdate 返回 false 时,不会调用 componentDidUpdate

\n
\n\n

来自文档

\n\n
\n

使用 shouldComponentUpdate() 让 React 知道组件\xe2\x80\x99s 的输出是否不受当前状态或属性更改的影响。

\n
\n\n

因此,如果您决定在更改时不更新组件this.props.widgetID,那么您就违反了假设/目的,shouldComponentUpdate并且不应期望您的小部件侦听器会被更新。

\n\n

如果你滥用的话,很多事情都不会按预期工作shouldComponentUpdate(例如,组件未更新以反映新数据),因此依赖按照官方文档正确使用 API 是实现简单性的必要条件,而不是要避免的事情。

\n\n
\n

在属性更改和更新完成之间的整个时间跨度内,侦听器将是错误的

\n
\n\n

通过这种逻辑,当您在事件处理程序中更新某些显示的数据时,您还可以声称在事件和重新渲染之间的整个时间跨度内显示的数据是错误的。您甚至可以声称您的文本编辑器在您按下键盘按键和在屏幕上呈现该按键之间显示了错误的数据。

\n