输入大量,但只有一个按钮提交到redux-saga

时间:2019-04-05 21:31:00

标签: javascript reactjs redux redux-saga

问题是我有多个输入和一个按钮来保存数据。我在我的组件中有此方法:

handleClick(e) {
    e.preventDefault();

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

此方法在我的redux传奇中捕获:

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 :(得分:0)

您需要使用Redux-Saga forkcancel

一步一步,针对您的特定实现的解决方案可能是以下方法:

  • redux-saga导入更多效果:import { cancel, fork} from "redux-saga/effects";
  • isEmail传奇在开始时就调度一个动作,以让其他sagas截获它,然后用reducer设置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)之类的动作
  • settingsPageSaga中产生/分叉一个新的传奇(它们之间的区别与您的示例代码无关,read here如果您想了解更多)
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传奇
    • saveDataNetworkFlow传奇故事等待100毫秒
    • 没有任何待处理的电子邮件检查,因此saveDataNetworkFlow称为saveData传奇
  • 具有有效AJAX检查的简单CHANGE_EMAIL操作

    • saveDataSaga传奇取消了以前的saveDataNetworkFlow传奇
    • SAVE_EMAIL将解锁saveDataNetworkFlow传奇
    • 没有待处理的电子邮件检查(请记住SAVE_EMAIL操作将一个reducer设置为emailCheckRunning为false)
    • saveDataNetworkFlow呼叫saveData传奇
  • 具有无效AJAX检查的简单CHANGE_EMAIL操作

    • saveDataSaga传奇取消了以前的saveDataNetworkFlow传奇
    • SAVE_EMAIL将解锁saveDataNetworkFlow传奇
    • 有一封待处理的电子邮件检查,因此saveDataNetworkFlow等待SAVE_EMAIL操作
    • SAVE_EMAIL不会被派发,saveDataNetworkFlow的传奇永无止境,最终将在未来被取消
  • CHANGE_NAMECHANGE_EMAIL具有有效的AJAX检查

    • saveDataSaga传奇取消了以前的saveDataNetworkFlow传奇
    • SAVE_NAME操作解锁了saveDataNetworkFlow传奇
    • saveDataNetworkFlow传奇故事等待100毫秒
    • 有一封待处理的电子邮件检查,因此saveDataNetworkFlow等待SAVE_EMAIL操作
    • isEmail传奇开始派发SAVE_EMAIL
    • saveDataNetworkFlow呼叫saveData传奇
  • CHANGE_NAMECHANGE_EMAIL具有无效的AJAX检查

    • saveDataSaga传奇取消了以前的saveDataNetworkFlow传奇
    • SAVE_NAME操作解锁了saveDataNetworkFlow传奇
    • saveDataNetworkFlow传奇故事等待100毫秒
    • 有一个待处理的电子邮件检查,因此saveDataNetworkFlow等待SAVE_EMAIL操作
    • SAVE_EMAIL不会被派发,saveDataNetworkFlow的传奇永无止境,最终将在未来被取消
  • CHANGE_NAMECHANGE_EMAIL操作在先前的传奇运行时分派

    • 您需要为isNameisEmail sagas实施相同的取消逻辑,以避免任何并发问题,在这里您可以找到how to do it(这是相同基础上的指南我的解决方案)

我希望它会有所帮助,如果您需要更多帮助,请告诉我


请注意:

  • 如果在第一个saveData仍在等待服务器响应的同时用户提交了两次表单,则两个AJAX请求将到达服务器。您可以使用一些化简工具来管理Redux状态,并避免用户在第一个请求尚未完成时提交表单。 取消AJAX请求没有任何意义,因为一旦它离开客户端,它可能已经在服务器上生效。
  • 每个已取消的英雄传奇都可以管理其取消政策,管理它的代码如下
import { cancelled } from 'redux-saga/effects'
function* saga() {
  try {
    // ... your code
  } finally {
    if (yield cancelled())
      // the saga has been cancelled
  }
}