Как обрабатывать после изменения состояния в диспетчере контекста React

У меня есть приложение React версии 16. Изменения состояния обрабатываются с использованием React Context, концепции редуктора.

Чтобы изменить состояние и сделать что-то, можно обработать это изменение, как указано ниже. Например - я нахожусь в компоненте «Контакт». Внутри этого я отправляю изменение состояния на переменный возраст, который представляет собой число, указанное ниже -

dispatch({
    type: "CHANGE_AGE",
    payload: age
})

и поймать отправку в редукторе, как указано ниже

case "CHANGE_AGE":
    return {
        ...state,
        age: action.payload
    }

Чтобы сделать что-то в компоненте «Контакт» после изменения возраста, мы можем использовать useEffect, чтобы отслеживать изменение «возраста».

useEffect({
    //doSomething()
},[state.age])

Проблема в следующем: что, если бы нам пришлось изменить объект в состоянии, т.е. вместо изменения возраста нам пришлось бы изменить адрес, как указано ниже, и мы должны были бы что-то сделать после изменения адреса.

Как мы следим за сменой адреса?

dispatch({
    type: "CHANGE_ADDRESS",
    payload: {
        city: "Delhi",
        country: "India",
        zip: '22233'
    }
})

Одно из возможных решений - наряду с рассылкой адресов, мы можем отправить переменную, при изменении которой мы можем что-то сделать. Но это не идеальное решение.


person bijoy tv    schedule 25.06.2020    source источник


Ответы (1)


Просто сохраните оба: предыдущий и текущий адрес

// your-state.js
const initialState = {
  age: 0,
  prevAdress: {
    city: '',
    country: '',
    zip: ''
  },
  currAdress: {
    city: '',
    country: '',
    zip: ''
  },
  ...
}

и когда вы отправляете, запишите текущий как предыдущий в редукторе

// your-reducer.js
...
case "CHANGE_ADDRESS":
    return {
        ...state,
        prevAddress: { ...state.currAddress },
        currAddress: { ...action.payload }
    }

Тогда сравните в эффекте

// your-component.js
...
useEffect(() => {
  if (state.prevAddress.zip !== state.currAddress.zip) { // just an example of comparison
    // doSmthg
  }
}, [state]);
...

ОБНОВЛЕНИЕ

Если вам нужно сравнить адрес только один раз и сделать что-то на основе этого сравнения, просто сравните перед отправкой значения:

// your-component.js
...
useEffect(() => {
  const doDispatch = () => dispatch({
        type: "CHANGE_ADDRESS",
        payload: {
          city: "Delhi",
          country: "India",
          zip: newZip,
        },
      });

  if (state.address.zip !== newZip) {
    doSmthgAsPromise().then(() => {     // do something and then dispatch
      doDispatch();
    });
  } else {
    doDispatch();
  }
}, [state, dispatch, doSmthgAsPromise]);
person Rostyslav    schedule 25.06.2020
comment
Хороший подход. Мой вопрос - что, если бы у нас было много переменных состояния, содержащих значения объектов, должны ли мы иметь предыдущие и текущие переменные для каждой? Вы можете придумать какой-нибудь другой подход? - person bijoy tv; 25.06.2020
comment
Ну, это зависит от вашей цели. Я думаю, вам нужно иметь prev / curr для каждого, если вы хотите сравнить их несколько раз в разное время. Если вы хотите просто проверить это один раз, просто посмотрите решение в моем обновлении - person Rostyslav; 25.06.2020
comment
Я вижу твое обновление. Единственный недостаток, который я вижу, это то, что он попадет во многие локальные переменные состояния, такие как newZip, которые мы должны сравнивать. Предполагая, что моей целью является обновление, и я мог бы потенциально обновить что-либо в этом адресном объекте И точно так же, как адресный объект, если у меня есть много других переменных состояния (типа объекта) ...? - person bijoy tv; 25.06.2020
comment
Я не уверен, какова ваша конечная цель. В React Context или useReducer ловушке нет истории или журнала изменений. Поэтому единственный способ отслеживать изменения - это где-то их хранить. - person Rostyslav; 25.06.2020