Много входов, но только одна кнопка представить в Redx-сага - PullRequest
0 голосов
/ 06 апреля 2019

Проблема в том, что у меня есть несколько входов и одна кнопка для сохранения данных. У меня есть этот метод в моем компоненте в реакции:

handleClick(e) {
    e.preventDefault();

    this.props.newEmail ? this.props.onSaveNewEmail(this.props.newEmail) : null;
    this.props.newName ? this.props.onSaveNewName(this.props.newName) : null;
  }

И этот метод отражен в моей саге о редуксе:

export function* isName() {
  const name = yield select(makeNewNameSelector());
  ...

  // validation name
  if (!re.test(name)) {
    ...
  } else {
    // ! OK, I CAN UPDATE NAME BECAUSE NEW NAME IS OK
  }
}

export function* isEmail() {
  const email = yield select(makeNewEmailSelector());
  const requestURL = `/api/users/isEmail/${email}`;
  ...

  // validation email
  if (!re.test(email)) {
    ...
  } else {
    try {
      const response = yield call(request, requestURL);
      if (!response.isEmail) {
        // ! OK, I CAN UPDATE EMAIL BECAUSE IT DOESN'T HAVE IN MY DB MYSQL
      }
    } catch (err) {
      ...
    }
  }
}

// this method send request PUT and this can update data:
function* saveData() {
  const name = yield select(makeNewNameSelector());
  const email = yield select(makeNewEmailSelector());
  ...

  try {
    const response = yield call(request, requestURL, {
      method: 'PUT',
      headers: {
        Accept: 'application/json',
        'Content-Type': 'application/json',
        Authorization: `Bearer ${jwt}`,
      },
      body: JSON.stringify({
        name,
        email,
      }),
    });

    if (response.success) {
      name ? yield put(enterNewNameSuccessAction()) : null;
      email ? yield put(enterNewEmailSuccessAction()) : null;
      yield put(saveDataSuccessAction());
    }
  } catch (err) {
    ...
  }
}

export default function* settingsPageSaga() {
  yield takeLatest(ENTER_NEW_NAME, isName);
  yield takeLatest(ENTER_NEW_EMAIL, isEmail);
}

А теперь как мне вызвать мой saveData() метод только один раз?

Если вместо комментариев я поставлю yield call(saveData), он будет работать, но это отправит два запроса ! Я хотел бы отправить только один запрос PUT.

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

1 Ответ

0 голосов
/ 09 апреля 2019

Вам нужно играть с Redux-Saga fork и cancel

Шаг за шагом, решение для вашей конкретной реализации может быть следующим:

  • импортировать еще несколько эффектов из redux-saga: import { cancel, fork} from "redux-saga/effects";
  • пусть сага isEmail отправляет действие в самом его начале, чтобы другие саги перехватили его, а редуктор установил флаг emailCheckRunning. Это действие может быть yield put(EMAIL_CHECK_START)
  • пусть SAVE_EMAIL обновит флаг emailCheckRunning до false с редуктором
  • вместо // ! OK, I CAN UPDATE NAME BECAUSE NEW NAME IS OK, отправить действие как yield put(SAVE_NAME)
  • вместо // ! OK, I CAN UPDATE EMAIL BECAUSE IT DOESN'T HAVE IN MY DB MYSQL, отправьте действие как yield put(SAVE_EMAIL)
  • in settingsPageSaga spawn / fork новой саги (разница между ними не относится к вашему примеру кода, читайте здесь , если вы хотите узнать больше об этом)
yield fork(saveDataSaga);
  • Ваша saveDataSaga сага будет выглядеть так
function* saveDataSaga() {
  let task;
  while(true) {
    // both ENTER_NEW_NAME and ENTER_NEW_EMAIL will start a new `saveDataNetworkFlow` saga
    yield take([ENTER_NEW_NAME, ENTER_NEW_EMAIL]);

    // but, before starting a new one, it cancels the previously forked `saveDataNetworkFlow` (if any)
    if(task) {
      yield cancel(task);
    }

    task = yield fork(saveDataNetworkFlow);
  }
}
  • и сага saveDataNetworkFlow следующая
function* saveDataNetworkFlow() {
  const action = yield take([SAVE_NAME, SAVE_EMAIL]);
  if(action.type === SAVE_NAME) {
    // wait some milliseconds to let the `isEmail` saga run
    yield wait(100);
  }

  // I assume that the `emailCheckRunning` flag is stored on top of your state, update it based on the shape of your own state
  const emailCheckRunning = yield select((globalState) => globalState.emailCheckRunning);

  if(emailCheckRunning) {
    yield take(SAVE_EMAIL);
  }

  yield call(saveData);
}



Давайте проанализируем различные случаи:

  • простой CHANGE_NAME действие

    • сага saveDataSaga отменяет любую предыдущую saveDataNetworkFlow сагу
    • действие SAVE_NAME открывает сагу saveDataNetworkFlow 1067 *
    • сага saveDataNetworkFlow ожидания 100 миллисекунд
    • нет ожидающих проверки электронной почты, поэтому saveDataNetworkFlow вызывает saveData saga
  • простое CHANGE_EMAIL действие с действительной проверкой AJAX

    • сага saveDataSaga отменяет любую предыдущую saveDataNetworkFlow сагу
    • SAVE_EMAIL откроет сагу saveDataNetworkFlow 1089 *
    • нет ожидающей проверки электронной почты (помните, что действие SAVE_EMAIL возвращает редуктору emailCheckRunning значение false)
    • saveDataNetworkFlow вызов саги saveData
  • простое CHANGE_EMAIL действие с неверной проверкой AJAX

    • сага saveDataSaga отменяет любую предыдущую saveDataNetworkFlow сагу
    • SAVE_EMAIL откроет сагу saveDataNetworkFlow 1112 *
    • есть ожидающая проверка электронной почты, поэтому saveDataNetworkFlow ожидает действия SAVE_EMAIL
    • SAVE_EMAIL не будет отправлено, сага saveDataNetworkFlow никогда не закончится и в конечном итоге будет отменена
  • CHANGE_NAME и CHANGE_EMAIL с действительным чеком AJAX

    • сага saveDataSaga отменяет любую предыдущую saveDataNetworkFlow сагу
    • действие SAVE_NAME открывает сагу saveDataNetworkFlow 1136 *
    • сага saveDataNetworkFlow ждет 100 миллисекунд
    • есть ожидающая проверка электронной почты, поэтому saveDataNetworkFlow ожидает действия SAVE_EMAIL
    • сага isEmail а SAVE_EMAIL
    • saveDataNetworkFlow вызов saveData саги
  • CHANGE_NAME и CHANGE_EMAIL с недействительной проверкой AJAX

    • сага saveDataSaga отменяет любую предыдущую saveDataNetworkFlow сагу
    • действие SAVE_NAME открывает сагу saveDataNetworkFlow 1167 *
    • сага saveDataNetworkFlow, ожидающая 100 миллисекунд
    • есть ожидающая проверка электронной почты, поэтому saveDataNetworkFlow ожидает действия SAVE_EMAIL
    • SAVE_EMAIL не будет отправлено, сага saveDataNetworkFlow никогда не закончится и в конечном итоге будет отменена
  • CHANGE_NAME или CHANGE_EMAIL действие отправлено во время выполнения предыдущей саги

    • вам нужно реализовать ту же логику отмены для саг isName и isEmail, чтобы избежать проблем с параллелизмом, здесь вы можете найти как это сделать (это то же самое руководство, которое есть в основе этого решения моего)

Надеюсь, это может быть полезно, дайте мне знать, если вам нужно что-то еще 11


Обратите внимание:

  • если пользователь отправляет форму дважды, в то время как первый saveData все еще ожидает ответа сервера ... два AJAX-запроса попадут на сервер. Вы должны управлять состоянием Redux с помощью некоторых редукторов и избегать отправки пользователем формы, пока первый запрос еще не завершен. Отменить запрос AJAX не имеет смысла, поскольку после того, как он покинул клиента, он уже может вступить в силу на сервере.
  • каждая отмененная сага может управлять своей политикой отмены, код для управления - следующий
import { cancelled } from 'redux-saga/effects'
function* saga() {
  try {
    // ... your code
  } finally {
    if (yield cancelled())
      // the saga has been cancelled
  }
}
...