【问题标题】:redux saga and history.pushredux saga 和 history.push
【发布时间】:2017-12-18 08:44:27
【问题描述】:

背景:
我正在创建一个Login 组件。

saga.js由3个函数组成
1.rootSaga。它将执行sagas里面的列表
2.watchSubmitBtn。它会观察提交按钮的点击并发送一个动作。
3.shootApiTokenAuth会接收派发的action并处理axios.post返回值为promise对象

实际操作:
后端将400 返回到React。这种情况没问题我可以轻松阅读payload并显示在render()中。但是当200 返回时。 我需要让用户转到 url /companies

尝试:
我曾尝试将this.props.history.push('/companies'); 放入componentWillUpdate(),但它不起作用。我必须点击 Submit 2 次才能让 React 明白 token 已被保存。

Login.js

import React, {Component} from 'react';
import ErrorMessage from "../ErrorMessage";
import {Field, reduxForm} from 'redux-form';
import {connect} from 'react-redux';
import {validate} from '../validate';
import {SUBMIT_USERNAME_PASSWORD} from "../../constants";

class Login extends Component {

  constructor(props) {
    //Login is stateful component, but finally action will change
    //reducer state
    super(props);
    const token = localStorage.getItem('token');
    const isAuthenticated = !((token === undefined) | (token === null));
    this.state = {
      token,
      isAuthenticated,
      message: null,
      statusCode: null
    };
  }

  onSubmit(values) {
    const {userid, password} = values;
    const data = {
      username: userid,
      password
    };
    this.props.onSubmitClick(data);
  }

  componentWillUpdate(){
    console.log('componentWillUpdate');
    if(this.props.isAuthenticated){
      this.props.history.push('/companies');
    }
  }

  renderField(field) {
    const {meta: {touched, error}} = field;
    const className = `'form-group' ${touched && error ? 'has-danger' : ''}`;

    console.log('renderField');

    return (
      <div className={className}>
        <label>{field.label}</label>
        <input
          className="form-control"
          type={field.type}
          placeholder={field.placeholder}
          {...field.input}
        />
        <div className="text-help">
          {touched ? error : ''}
        </div>
      </div>
    );
  }

  render() {
    const {handleSubmit} = this.props;

    return (
      <div>
        <ErrorMessage
          isAuthenticated={this.props.isAuthenticated}
          message={this.props.message}
        />

        <form onSubmit={handleSubmit(this.onSubmit.bind(this))}>
          <Field
            name="userid"
            component={this.renderField}
            placeholder="User ID"
            type="text"
          />
          <Field
            name="password"
            component={this.renderField}
            placeholder="Password"
            type="password"
          />
          <button type="submit" className="btn btn-primary">Submit</button>
        </form>
        <a className='btn btn-primary' href="https://www.magicboxasia.com/">Sign up</a>
      </div>
    );
  }
}


const onSubmitClick = ({username, password}) => {
  return {
    type: SUBMIT_USERNAME_PASSWORD,
    payload: {username, password}
  };
};

const mapStateToProps = (state, ownProps) => {
  return {
    ...state.login
  }
};

export default reduxForm({
  validate,
  form: 'LoginForm'
})(
  connect(mapStateToProps, {onSubmitClick})(Login)
);

saga.ja

const shootApiTokenAuth = (values) =>{
  const {username, password} = values;
  return axios.post(`${ROOT_URL}/api-token-auth/`,
    {username, password});
};

function* shootAPI(action){
  try{
    const res = yield call(shootApiTokenAuth, action.payload);
    yield put({
      type: REQUEST_SUCCESS,
      payload: res
    });
  }catch(err){
    yield put({
      type: REQUEST_FAILED,
      payload: err
    });
  }
}

function * watchSubmitBtn(){
  yield takeEvery(SUBMIT_USERNAME_PASSWORD, shootAPI);
}

// single entry point to start all Sagas at once
export default function* rootSaga() {
  yield all([
    watchSubmitBtn()
  ])
}

问题:
如何将组件状态和push 设置为 url /companies?后端返回后200?

【问题讨论】:

标签: javascript reactjs redux-form redux-saga


【解决方案1】:

我通常会像 saga 中那样处理条件导航。

现有代码的最简单答案是在 SUBMIT_USERNAME_PASSWORD 操作中将历史对象作为道具传递,并在 saga 的成功案例中执行 history.push() 调用,例如:

const onSubmitClick = ({username, password}) => {
  const { history } = this.props;

  return {
    type: SUBMIT_USERNAME_PASSWORD,
    payload: {username, password, history}
  };
};

.......

function* shootAPI(action){
  try{
    const res = yield call(shootApiTokenAuth, action.payload);
    const { history } = action.payload;

    yield put({
      type: REQUEST_SUCCESS,
      payload: res
    });

    history.push('/companies');
  }catch(err){
    yield put({
      type: REQUEST_FAILED,
      payload: err
    });
  }
}

【讨论】:

  • 我只知道我可以通过历史!非常感谢你。我总是以this 为前缀的每个属性都无法传递给其他文件。但我错了。
  • 我希望有更好的方法来做到这一点。如在 Angular 中,您可以直接在 saga/effect 类中注入路由器/历史记录。所以我们不需要传递历史。
  • 你是天才@brub
  • @brub 你不觉得这是一个黑客吗?我的意思是在动作中发送函数和对象(如历史记录)似乎不是预期的事情。不幸的是,我不能用我自己的理由来证明它的合理性(尽管 Redux-toolkit 对此有意见 - redux.js.org/style-guide/…
  • 正如我之前在回复@dasfdsa 时提到的,我没有通过函数传递历史记录,这只是基于提供的代码的简单示例。通常我用一个历史初始化我的路由器,然后我可以导入到 sagas 中。
【解决方案2】:
import { push } from 'react-router-redux';    

yield put(push('/path-to-go'));

解决了我的问题

【讨论】:

  • 这个可行,但是 react-router-redux 不再维护,应该使用 connected-react-router
【解决方案3】:

我在反应方面没有经验,但您可以通过以下方式实现:

1.创建一个新模块:history-wrapper.ts

export class HistoryWrapper {
  static history;
  static init(history){
    HistoryWrapper.history = history;
  }
}

2.在你login.jsx

  HistoryWrapper.init(history);//initialize history in HistoryWrapper

3.随后在您的应用中的任何位置

  HistoryWrapper.history.push('/whatever');

【讨论】:

  • 在实践中,我不会将历史作为道具传递,我使用与您使用github.com/ReactTraining/history 类似的方法。我使用这个历史对象初始化我的路由器,然后我将它导入到我的 sagas 中并直接使用它。
【解决方案4】:

React 有额外的生命周期。我今天才知道。他们很多。

componentDidUpdate() {
    this.props.history.push('/companies');
  }

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2017-11-14
    • 2017-01-23
    • 1970-01-01
    • 1970-01-01
    • 2021-12-22
    • 2017-05-28
    • 1970-01-01
    • 2018-01-22
    相关资源
    最近更新 更多