Как правильно обрабатывать подписки в зависимости от свойства - PullRequest
0 голосов
/ 06 мая 2018

У меня есть глобальная служба widgetService, которая содержит данные для ряда виджетов, каждый из которых идентифицируется как widgetID. Данные каждого виджета могут измениться в любое время. Я хочу отобразить виджет с компонентом React, скажем WidgetReactComponent.

Реактивный компонент должен взять идентификатор виджета в качестве свойства и получить информацию, отображаемую из службы виджетов. Данные виджета могут быть запрошены из сервиса виджетов методом 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 может измениться в любое время, и компонент должен обработать это, чтобы нормально функционировать при любых обстоятельствах. В соответствии с рекомендацией реагирования это должно быть выполнено путем установки состояния на основе свойств с помощью функции 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 голосов
/ 09 мая 2018

Вам не нужно хранить 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 не будет вызываться, когда shouldComponentUpdate возвращает false

Из документов :

Используйте shouldComponentUpdate (), чтобы сообщить React, если на выход компонента не влияет текущее изменение состояния или параметров.

Таким образом, если вы решили не обновлять компонент при изменении this.props.widgetID, то вы нарушаете предположение / цель shouldComponentUpdate и не должны ожидать, что ваш слушатель виджета будет обновлен.

Многие вещи не будут работать должным образом, если вы все равно будете неправильно использовать shouldComponentUpdate (например, компонент не обновлен для отображения новых данных), поэтому для достижения простоты необходимо полагаться на API, используемый в соответствии с официальными документами, а не то, чего следует избегать.

слушатели будут ошибаться в течение всего промежутка времени между изменением свойства и завершением обновления

По этой логике, когда вы обновляете некоторые отображаемые данные в обработчике событий, вы также можете утверждать, что отображаемые данные неверны в течение всего промежутка времени между событием и повторной визуализацией. Вы даже можете утверждать, что ваш текстовый редактор отображает неправильные данные между временем нажатия клавиши клавиатуры и отображением клавиши на экране.

...