如何为传奇提供历史实例?

How to provide a history instance to a saga?

我想在成功登录后重定向到一个新页面。 路由(V4)是这样使用的:

import { browserHistory } from '....browser_history_signleton';
...

class App extends Component {
  render() {
    const { authentication: { isSignedIn } } = this.props;
    return (
      <ConnectedRouter history={browserHistory}>
        <div>
          <Header/>
          <Route exact path="/" component={Home}/>
          <PrivateRoute isAuthorized={isSignedIn} path="/page1" component={PageOne}/>
          <PrivateRoute isAuthorized={isSignedIn} path="/page2" component={PageTwo}/>
        </div>
      </ConnectedRouter>
    );
  }
}

传奇看起来像:

import { browserHistory } from '....browser_history_signleton';

export function* loginSaga() {
  while (true) { // eslint-disable-line no-constant-condition
    try {
      const payload = yield take(LOGIN_SUBMIT);
      const raceResult = yield race({
        signin: call(loginRequest, payload),
        logout: take('LOGOUT')
      });
      if (raceResult.signin) {
        const { data }  = raceResult.signin;
        yield put(loginRequestSucceeded(data));
        const redirectUrl = `.....based on location.state.from.pathname`
        browserHistory.push(rediretUrl);
        ...

我的主要问题是如何分享browserHistoryhistory 模块中的 createHistory 不是单例,所以我必须添加:

// browser_history_signleton.js
import createHistory from 'history/createBrowserHistory';

export const browserHistory = createHistory();

向 saga 提供 history 实例的最有效方法是什么?

正如您所指出的,让历史直接作为单例可用是一种选择。另一种类似的(如果没有争议的话)方法是将历史记录添加到 redux 状态。因此,在您的传奇中,您将使用 select 效果来获取历史实例。

这两种方法都有效地将历史视为单例,但通过将其存储到 redux 状态,您可以在单元测试中更轻松地模拟历史对象。我所说的更简单的意思是,您可以使用相同的技术来模拟历史对象,就像模拟 redux 状态中的任何其他对象一样。

此外,您可能会将历史对象与其他初始状态一起实例化,这样您初始化历史的逻辑就不会成为应用程序状态初始化方式的一次性异常。

我找到了两个感觉不错的选项,并且都使用了。我很好奇是否有人对这两者有任何问题。

选项 1:将 history 对象传递给 sagas。

它并不明显,但是 sagaMiddleware.run 函数采用第二个参数,该参数被转发到 sagas。即:

/wherever/you/start/saga.js

import { createBrowserHistory } from "history";
import saga1 from "./saga1.js";

const  function* rootSaga({ history }) {
  yield all([saga1({ history })])
}

const sagaTask = sagaMiddleware.run(rootSaga, { history: createBrowserHistory() });

我在这里学到了这个:https://github.com/ReactTraining/react-router/issues/3972#issuecomment-251189856

这是访问历史记录功能的简洁方式。在您实际的 sagas 中,您会像往常一样使用 history 对象。

./saga1.js

export default ({ history }) => [
  takeEvery(actions.DO_SOMETHING_THEN_NAVIGATE, function*({ payload }) {
    ...do something
    history.push("/somewhere");
  }),
];

选项 2:让单个 saga 管理 history 对象并使用操作进行导航

这是选项 1 的扩展。将传奇故事献给 "manage" 历史对象 - pushing/replacing 使用操作。即:

/my/history/saga.js


export default ({ history }) => [ // history is passed in ala option 1.
  takeEvery(actions.HISTORY_PUSH, function*({ payload }) {
    const pathname = payload.fooParam;
    yield history.push(pathname);
  }),
  takeEvery(actions.HISTORY_REPLACE, function*({ payload }) {
    yield history.replace({ pathname: payload.barParam });
  }),
];

这可以让你的 redux store 和 actions 保持干净,没有社区中一些 提出的怪异 hacks - 比如在 actions 中传递历史对象。

告诉我你的想法。