Предотвращение повторного рендеринга для контролируемых входных данных в приложении React Flux

В моем приложении есть классический шаблон Flux, использующий fluxible. value входа привязано к пропсу моего компонента, а его onChange вызывает действие, которое, в свою очередь, обновляет хранилище, которое, в свою очередь, передает проп в мой компонент:

class MyComponent extends React.Component {
  handleChange(e) {
    this.context.executeAction(persist, { value: e.target.value });
  }

  render() {
    return <input value={this.props.value} onChange={this.handleChange.bind(this)}/>;
  }
}

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

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

Существует ли хорошо зарекомендовавшая себя схема предотвращения повторного рендеринга для контролируемых входных данных?


person Jacob    schedule 02.02.2016    source источник
comment
Для меня это было давно, но я помню, что все мои формы управляли своим собственным состоянием, чтобы предотвратить его изменение, когда пользователь редактировал его. Возможно, вы могли бы установить значение состояния только тогда, когда пользователь ввел что-то, что не равно this.props.value. Просто мысль.   -  person Jason Rice    schedule 02.02.2016
comment
Спасибо @JasonRice, я пришел к очень похожему выводу.   -  person Jacob    schedule 02.02.2016


Ответы (1)


Я нашел описание ReactLink, чтобы дать довольно хорошее образец обращения с подобными вещами. Вот что я придумал:

class MyComponent extends React.Component {
  constructor(props) {
    super(props);
    this.state = { value: props.value };
    this.handleChange = this.handleChange.bind(this);
  }

  componentWillReceiveProps(newProps) {
    // If in some cases you want to handle a new value prop, update state here
  }

  handleChange({ target: { value } }) {
    this.setState({ value });
    this.context.executeAction(persist, { value });
  }

  render() {
    return <input value={this.state.value} onChange={this.handleChange}/>;
  }
}

По сути, просто игнорируйте людей, которые говорят: «Если вы устанавливаете состояние на основе реквизита, вы делаете это неправильно», и устанавливайте исходное состояние на основе реквизита. Это состояние - то, что ваш ввод теперь использует для своего значения. Затем, предполагая, что новые реквизиты value поступят, они не повлияют на ваш вывод render. Если вы этого хотите, вы можете добавить логику к componentWillReceiveProps. В моем случае, например, я хотел обновить ввод, но только если он не был сфокусирован:

componentWillReceiveProps({ value }) {
  if (!this.state.isFocused) {
    this.setState({ value });
  }
}
person Jacob    schedule 02.02.2016
comment
Проблема с использованием isFocused заключается в том, что пользователь отредактирует ввод, а затем перейдет к следующему полю только для того, чтобы его предыдущая работа была отменена, что было бы неприятно. Если бы это был я, я бы добавил кнопку сброса, которая просто очищает состояние или повторно инициализирует его. (Я предполагаю, что это предполагает несколько полей и кнопку отправки) - person Jason Rice; 04.02.2016
comment
Это отличный момент, @JasonRice. В моем случае эти иностранные обновления свойств исходят от процесса синхронизации, который извлекает данные из другого источника в непредсказуемое время. Возможно, лучшая проверка для этого сценария состоит в том, чтобы заменить содержимое только в том случае, если что-то не было введено. Я пришел к выводу, что, вероятно, не существует хорошего универсального сценария, поэтому необходим пользовательский componentWillReceiveProps. - person Jacob; 05.02.2016