Redux: 在响应路由器中触发响应路径转换的动作

创建于 2015-07-07  ·  26评论  ·  资料来源: reduxjs/redux

嗨,大家好,

我在最新的应用程序中使用react-router和redux,并且遇到了一些与基于当前url参数和查询所要求的状态更改有关的问题。

基本上,我有一个组件,每次URL更改时都需要更新其状态。 像这样通过装饰器通过redux通过props传递状态

 @connect(state => ({
   campaigngroups: state.jobresults.campaigngroups,
   error: state.jobresults.error,
   loading: state.jobresults.loading
 }))

目前,我正在使用componentWillReceiveProps生命周期方法来响应来自react-router的url更改,因为当url中的this.props.paramsthis.props.query更改时,react-router会将新的props传递给处理程序。

  componentWillReceiveProps(nextProps) {
    if (this.state.shouldupdate) {
      let { slug } = nextProps.params;
      let { citizenships, discipline, workright, location } = nextProps.query;
      const params = { slug, discipline, workright, location };
      let filters = this._getFilters(params);
      // set the state accroding to the filters in the url
      this._setState(params);
      // trigger the action to refill the stores
      this.actions.loadCampaignGroups(filters);
    }
  }

是否有基于路线转换触发动作的标准方法,还是可以将商店的状态直接连接到组件的状态,而不是通过prop传递它? 我尝试使用willTransitionTo静态方法,但是无法访问this.props.dispatch。

docs ecosystem question

最有用的评论

@deowk ,这个问题有两个部分。 第一个是componentWillReceiveProps()不是响应状态变化的理想途径-主要是因为它迫使你认为势在必行,而不是被动地像我们与终极版做。 解决方案是在商店中存储您当前的路由器信息(位置,参数,查询)。 然后,您所有的状态都在同一个地方,您可以使用与其余数据相同的Redux API对其进行订阅。

诀窍是创建一个在路由器位置更改时将触发的操作类型。 在即将推出的React Router 1.0版本中,这很容易:

// routeLocationDidUpdate() is an action creator
// Only call it from here, nowhere else
BrowserHistory.listen(location => dispatch(routeLocationDidUpdate(location)));

现在,您的存储状态将始终与路由器状态同步。 这就解决了手动响应查询参数更改和上面组件中的setState() -只需使用Redux的连接器即可。

<Connector select={state => ({ filter: getFilters(store.router.params) })} />

问题的第二部分是您需要一种对视图层外部的Redux状态更改做出反应的方法,例如响应路径更改而触发操作。 如果需要,您可以继续使用componentWillReceiveProps处理简单情况,例如您描述的情况。

但是,对于更复杂的事情,我建议您使用RxJS(如果您愿意的话)。 这正是可观察对象设计的目的-反应性数据流。

要在Redux中执行此操作,请首先创建一个可观察的存储状态序列。 您可以使用redux-rx的observableFromStore()

import { observableFromStore } from 'redux-rx';
const state$ = observableFromStore(store);

然后,只需使用可观察的运算符来订阅特定的状态更改即可。 这是成功登录后从登录页面重定向的示例:

const didLogin$ = state$
  .distinctUntilChanged(state => !state.loggedIn && state.router.path === '/login')
  .filter(state => state.loggedIn && state.router.path === '/login');

didLogin$.subscribe({
   router.transitionTo('/success');
});

与使用componentDidReceiveProps()等命令性模式的相同功能相比,此实现要简单得多。

希望有帮助!

所有26条评论

@deowk您可以将this.propsnextProps进行比较,以查看相关数据是否发生了变化。 如果它没有改变,那么您不必再次触发该动作,就可以摆脱无限循环。

@johanneslumpe :在我的情况下,campaigngroup是一个相当大的对象集合,以这种方式使用深度对象比较作为条件似乎效率不高?

@deowk如果使用不可变数据,则可以只比较引用

@deowk ,这个问题有两个部分。 第一个是componentWillReceiveProps()不是响应状态变化的理想途径-主要是因为它迫使你认为势在必行,而不是被动地像我们与终极版做。 解决方案是在商店中存储您当前的路由器信息(位置,参数,查询)。 然后,您所有的状态都在同一个地方,您可以使用与其余数据相同的Redux API对其进行订阅。

诀窍是创建一个在路由器位置更改时将触发的操作类型。 在即将推出的React Router 1.0版本中,这很容易:

// routeLocationDidUpdate() is an action creator
// Only call it from here, nowhere else
BrowserHistory.listen(location => dispatch(routeLocationDidUpdate(location)));

现在,您的存储状态将始终与路由器状态同步。 这就解决了手动响应查询参数更改和上面组件中的setState() -只需使用Redux的连接器即可。

<Connector select={state => ({ filter: getFilters(store.router.params) })} />

问题的第二部分是您需要一种对视图层外部的Redux状态更改做出反应的方法,例如响应路径更改而触发操作。 如果需要,您可以继续使用componentWillReceiveProps处理简单情况,例如您描述的情况。

但是,对于更复杂的事情,我建议您使用RxJS(如果您愿意的话)。 这正是可观察对象设计的目的-反应性数据流。

要在Redux中执行此操作,请首先创建一个可观察的存储状态序列。 您可以使用redux-rx的observableFromStore()

import { observableFromStore } from 'redux-rx';
const state$ = observableFromStore(store);

然后,只需使用可观察的运算符来订阅特定的状态更改即可。 这是成功登录后从登录页面重定向的示例:

const didLogin$ = state$
  .distinctUntilChanged(state => !state.loggedIn && state.router.path === '/login')
  .filter(state => state.loggedIn && state.router.path === '/login');

didLogin$.subscribe({
   router.transitionTo('/success');
});

与使用componentDidReceiveProps()等命令性模式的相同功能相比,此实现要简单得多。

希望有帮助!

我们在新文档中需要它。 写得很好。

@acdlite :非常感谢,您的建议确实对我有很大帮助,尽管如此,我仍在努力--通过触发动作创建者以响应URL更改来更改商店中的状态。

我想在willTransitionTo静态方法中触发动作创建者,因为这似乎是react-router v0.13.3中的唯一选项

class Results extends Component  {
..........
}

Results.willTransitionTo = function (transition, params, query) {
 // how do I get a reference to dispatch here?
};

@deowk我的猜测是,您直接在redux实例上调用dispatch

const redux = createRedux(stores);
BrowserHistory.listen(location => redux.dispatch(routeLocationDidUpdate(location)));

@deowk我目前在反应路由器0.13.3中分派我的网址更改,如下所示:

Router.run(routes, Router.HistoryLocation, function(Handler, locationState) {
   dispatch(routeLocationDidUpdate(locationState))
   React.render(<Handler/>, appEl);
});

@acdlite真的很好解释! :+1:
同意它也应该在新文档中:)

只是注意, BrowserHistory.history()已更改为BrowserHistory.addChangeListener()

https://github.com/rackt/react-router/blob/master/modules/BrowserHistory.js#L57

编辑:

可能看起来像这样:

history.addChangeListener(() => store.dispatch(routeLocationDidUpdate(location)));

那个减速器@pburtchaell的初始状态又如何呢?

我有以下设置:

// client.js
class App extends Component {
  constructor(props) {
    super(props);
    this.history = new HashHistory();
  }

  render() {
    return (
      <Provider store={store}>
         {renderRoutes.bind(null, this.history)}
      </Provider>
    );
  }
}

// routes.js
export default function renderRoutes(history) {

  // When the route changes, dispatch that information to the store.
  history.addChangeListener(() => store.dispatch(routeLocationDidUpdate(location)));

  return (
    <Router history={history}>
      <Route component={myAppView}>
        <Route path="/" component={myHomeView}  />
      </Route>
    </Router>
  );
};

每次路线更改和应用首次加载时,都会触发routeLocationDidUpdate()操作创建者。

@pburtchaell您可以分享您的routeLocationDidUpdate吗?

@gyzerok当然。 它是一个动作创造者。

// actions/location.js
export function routeLocationDidUpdate(location) {
  return {
    type: 'LOCATION_UPDATE',
    payload: {
      ...location,
    },
  };
}

@pburtchaell,所以在您的示例中,我不会安静地了解您在哪里获取特定路由的数据必需数据

这是我上面的示例的完整版本:

https://github.com/acdlite/redux-react-router/blob/master/README.md#bonus -reacting-to-state-changes-with-redux-rx

我假设@pburtchaell将使用该代码的异步版本来获取数据(从REST调用等)。 我不确定那是什么? 我假设它会去一家商店,但那家商店会成为locationStore我

<strong i="7">@connect</strong>

进来说

<Comments />

哪些已经“连接”(订阅)到commentStore? 还是仅在commentStore中添加注册这些位置操作?

像这样的网址

/comments/123

总是会被“耦合”到评论组件,那么我该如何重用它呢? 对不起,这很愚蠢,在这里很困惑。 我可以找到一个可靠的例子。

我也在对此进行努力,弄清楚如何调用我的静态fetchData(dispatch, params)方法(静态,因为服务器在初始渲染之前调用它来调度异步FETCH动作)。

由于上下文中存在对dispatch的引用,因此我想为fetchData客户端调用获取此内容的最简单方法是类似Connector的组件,该组件调用fetchDatashouldFetchData ,或者让select回调获得对dispatch的引用以及state ,因此我们可以检查当前状态为store并根据需要调度FETCH操作。

后者更为简洁,但打破了select应该保持纯函数的事实。 我将研究前者。

我的解决方案是这样的,尽管它非常适合我的设置(静态fetchData(dispatch, state)方法调度异步_FETCH操作),但它会使用适当的属性调用传递给它的所有回调: https:// gist.github.com/grrowl/6cca2162e468891d8128 —但是,由于不使用willTransitionTo,因此您将无法延迟页面的转换。

我们绝对需要添加文档! 在“使用react-router使用”部分中可能。

1.0版发布后,我们将提供一种正式的方法。

很高兴听到@gaearon。

然后,只需使用可观察的运算符来订阅特定的状态更改即可。

我已经设置了一个可观察的流,以使我的商店与任何路线更改保持同步,并且我也有一个流设置来监视我的商店。 当我的商店中的值发生更改时,尤其是由于成功在我的应用程序中的其他位置提交表单而使它从undefined变为有效字符串时,我有兴趣过渡到新路线。

这两个流已设置并正常工作,并且正在更新存储,但是我对Rx还是陌生的,并且在可观察的查询中遇到了麻烦...任何指针? 我在正确的轨道上吗? 可以肯定的是,它与distinctUntilChanged但是目前正在烘烤我的面条,任何帮助表示赞赏:)

编辑:阿克! 对不起,回答了我自己的问题。 下面的伪代码。 让我知道它看起来是否很恐怖?

const didChangeProject$ = state$
  .distinctUntilChanged(state => state.project._id)
  .filter(state => typeof state.project._id !== 'undefined');

以#177结束。 在记录路由时,我们需要涵盖所有情况:状态更改时重定向,请求回调时重定向等。

我知道这是关闭的..但是现在使用React 0.14和beta中的各种1.0依赖项,是否对此进行了更新,如果没有,可以围绕React 0.14,react-router,redux等新功能进行教程吗?书面? 我们当中似乎有很多人试图同时了解/理解所有这些,以跟上即将发生的变化。 我知道事情处于不断变化的状态(双关..呃。与路由。 很可能是我自己的错,因为我仍在努力理解所有这些工作原理,只是希望很快就可以得到包含最新内容的更新教程。

除非有教程,否则请随意看一下具有路由的examples/real-world 。 现在还不是“最新和最好的”,但可以正常工作。

此页面是否有帮助?
0 / 5 - 0 等级

相关问题

EricSimons picture EricSimons  ·  45评论

ghost picture ghost  ·  50评论

timdorr picture timdorr  ·  56评论

emmenko picture emmenko  ·  66评论

gaearon picture gaearon  ·  69评论