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

标签 javascript reactjs listener publish-subscribe react-props

我有一个全局服务 widgetService,它保存着许多小部件的数据,每个小部件都由一个 widgetID 标识。每个小部件的数据都可以随时更改。我想显示一个带有 React 组件的小部件,比如 WidgetReactComponent

React 组件应将小部件 ID 作为属性,并从小部件服务获取要显示的信息。可以使用方法 getWidgetData(widgetID) 从 widget 服务查询 widget 的数据。并且为了能够发布数据变化,它还提供了两个方法: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);
    }
}

但是,当 shouldComponentUpdate 返回 false 时,不会调用 componentDidUpdate。感觉这不是一种安全的方法。此外,我相信在属性更改和更新完成之间的整个时间跨度内,听众都是错误的。我怎样才能安全地实现它?

最佳答案

你不需要在状态中存储widgetID,你可以比较prevPropsthis.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 的假设/目的,并且不应期望您的小部件要更新的监听器。

如果您无论如何都滥用 shouldComponentUpdate(例如,组件未更新以反射(reflect)新数据),很多事情将无法按预期工作,因此依赖于按照官方文档正确使用的 API 是一个实现简单性的必要性,而不是要避免的事情。

the listeners will be wrong for the entire timespan between the property change and the update's completion

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

关于javascript - 如何根据属性正确处理订阅,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/50204266/

相关文章:

javascript - WebSocketGateway 中的 WsException 不起作用

javascript - 如何使用ckEditor中的Gists代码片段功能?

JavaScript 函数和原型(prototype)

javascript - 无法读取未定义的属性 'initSdk' - Appsflyer 集成 React Native

java - Java中的文件更改监听器

java - 如何在对象类中的变量更改上创建事件

javascript - 我想更改选择框的文本而不干扰 "option"标签值

javascript - 通过具有已知键名的 props 访问数据 React js

javascript - Web Worker 不与 React 组件通信

c++ - 监听器类继承者 - 对监听类的 const 或非常量引用?