如何根据属性

时间:2018-05-06 20:32:16

标签: javascript reactjs listener publish-subscribe 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>;
    }
}

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

<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。这不是一种安全的方式。此外,我认为听众在属性更改和更新完成之间的整个时间跨度都是错误的。我怎么能安全地实现这个呢?

1 个答案:

答案 0 :(得分:1)

您不需要将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);
}

关于您的疑虑:

  

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

来自docs

  

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

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

如果你滥用shouldComponentUpdate(例如,组件未更新以反映新数据),许多事情将无法按预期工作,因此依据官方文档正确使用API​​是必要的简单,而不是要避免的东西。

  

在属性更改和更新完成之间的整个时间段内,听众都会出错

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