ReactJS: сохранение входного значения при размытии, а не при каждом нажатии клавиши - PullRequest
0 голосов
/ 26 ноября 2018

Я создал React View, скажем MyView, в котором есть 2 текстовых ввода, начальные значения которых будут переданы родительским чтением из БД.

Я также хочу, чтобы измененные значения были сохранены обратно вDB.Таким образом, представлению также передается функция обратного вызова для того же самого.

Учтите, что операция сохранения в БД тяжелая, и вам не следует делать это очень часто .Поэтому я решил прослушивать onBlur события вместо onChange событий в полях ввода, так как onChange вызывается при каждом нажатии клавиши.

Первый подход:

class MyView extends React.Component {
    constructor(props) {
        super(props);
    }

    render() {
        return (
            <div>
              <input type="url" value={this.props.values.A}
                   onBlur={(evt)=>{this.props.saveValue('A', evt.target.value)}} />
              <input type="url" value={this.props.values.B}
                   onBlur={(evt)=>{this.props.saveValue('B', evt.target.value)}} />         

              <button type="button" onClick={this.props.resetValues}>Reset</button>
            </div>
        );
     }
}

Однако это не работает, поскольку React принудительно заставляет управляемый вход (с атрибутом value) всегда сопровождаться прослушивателем onChange.

Второй подход:

Итак, я попытался сделать эти входные данные как uncontrolled.То есть вместо атрибута value используется defaultValue.

<input type="url" defaultValue={this.props.values.A}
       onBlur={(evt)=>{this.props.saveValue('A', evt.target.value)}} />

Но это также не сработало, как при нажатии кнопки сброса / очистки, хотя представление было выполнено для повторного рендеринга, но defaultValue не обновляется после создания представления.

Третий подход:

Итак, я наконец-то добавил onChange слушатель, но как безоперационный.

<input type="url" value={this.props.values.A}
       onChange={()=>{console.log('do nothing')}
       onBlur={(evt)=>{this.props.saveValue('A', evt.target.value)}} />

Опять же, это не сработало, так как представление перерисовывается после вызова onChange, и, поскольку значение не отражается в props, value, похоже, сбрасывается обратно к исходному при каждом нажатии клавиши.

Четвертый подход:

Последнее, что я пытался, было сохранить state в компоненте и считывать значение из состояния, а при каждом onChange сохранять значение обратно в состояние.В большинстве случаев это работало, но при любых внешних изменениях в подпорках и перерисовывании представления state не обновлялось.Итак, я добавил getDerivedStateFromProps функцию для просмотра:

static getDerivedStateFromProps(props, state) {
    return props.values;
}

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

Может ли какой-нибудь эксперт ReactJS помочь мне с моим вариантом использования?

Ответы [ 3 ]

0 голосов
/ 26 ноября 2018

Итак, с учетом того, что onChange работает, я бы порекомендовал вам взглянуть на это:

https://schier.co/blog/2014/12/08/wait-for-user-to-stop-typing-using-javascript.html Перейдите к заголовку: Ожидание набора текста до остановки

Надеюсь, это может как-то привести вас к тому, чего вы хотите достичь.

0 голосов
/ 26 ноября 2018

Исходя из комментариев к решению Liren Yeo, я бы обработал согласование состояния реквизита на componentDidUpdate, где вы получите и старые state и props.Таким образом, вы можете определить, как this.props был обновлен и действовать соответственно.Если значение в props не соответствует state или oldProps, обновление является внешним, и вам следует переопределить несохраненные изменения в состоянии.

Код должен выглядеть примерно так

componentDidUpdate(prevProps) {

if (this.props.values !== prevProps.values && this.props.values !== this.state.values) {
  this.setState({values:this.props.values});
   }
}

Если вы идете по этому маршруту, вы также можете оставить ввод uncontrolled и обновить его значение через ссылку.Это решает некоторую ненадежность с помощью controlled входных данных, например, type='number', возвращающих undefined в качестве значения при вводе десятичной запятой.Вам по-прежнему необходимо сохранить значение onChange, но сохранить его только onBlur и обработать согласование состояния и поддержки в componentDidUpdate

0 голосов
/ 26 ноября 2018

Вам все еще понадобится onChange, чтобы помочь вам установить состояния обоих URL-адресов.onBlur используется только для запуска сохранения, это 2 разных события для разных целей.

Поскольку ваши значения A & B передаются из родительского компонента.Родительский компонент MyView должен передавать this.state.values и функции для установки состояния.

См. Этот фрагмент, если все в одном компоненте.Вы должны быть в состоянии переместить функцию handleChange к ее родительскому компоненту.

class App extends React.Component {

  state = {
    values: {
      A: '',
      B: ''
    }
  }

  handleChange = e => {
    this.setState({
      values: {
      ...this.state.values,
      [e.target.name]: e.target.value
    })
  }

  handleBlur = e => {
    if (e.target.name === 'A') {
      alert(`Saving A: ${this.state.values.A}`)
    }

    if (e.target.name === 'B') {
      alert(`Saving B: ${this.state.values.B}`)
    }
  }

  render() {
    return (
      <div>
        <label>Value A</label>
        <input
          type="url"
          name="A"
          value={this.state.values.B}
          onChange={this.handleChange}
          onBlur={this.handleBlur}
        />
        <label>Value B</label>
        <input
          type="url"
          name="B"
          value={this.state.values.A}
          onChange={this.handleChange}
          onBlur={this.handleBlur}
        />
      </div>
    )
  }
}

ReactDOM.render(
  <App />,
  document.getElementById('container')
);
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/15.1.0/react.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/15.1.0/react-dom.min.js"></script>
<div id="container">
</div>

РЕДАКТИРОВАТЬ: Ваш четвертый подход должен работать со следующим:

static getDerivedStateFromProps(props, state) {
  return { values: props.values }
}

constructor(props) {
  super(props)
  this.state = {
    values: props.values
  }
}

, поэтому в основномthis.state.values является конечным источником истины.Когда пользователь что-то печатает, вы setState в этом компоненте и меняете его.Но если props.values изменится (из внешнего источника), getDerivedStateFromProps обновит состояние значений.

...