【问题标题】:How and where to dispatch action when react router changes props反应路由器更改道具时如何以及在何处调度动作
【发布时间】:2018-06-06 12:01:27
【问题描述】:

我可能在这里遗漏了一些东西,使用 Link 更改 react-router 参数将导致 props 随新参数而更改并触发更新周期。

您不能在更新周期中调度操作,但这是唯一可用参数的地方。当无状态组件中的 day 参数发生变化时,我需要获取新数据。

该组件有 2 个链接,前一天和后一天。前一天是这样的:

<Link to={"/sessions/" + prefDay}>{prefDay}</Link>

[更新]

目前的解决方案如下:

Overview 组件只是一个获取 props 并返回 jsx 的函数,以下是检查日期是否已设置的容器,如果未设置则重定向。如果设置了日期,那么它将返回概览。

它还会检查路由器日期参数是否更改,如果更改,则将 dispatchFetch 设置为 true。

这将导致渲染函数异步调度 getData 操作。

不确定是否有其他方法可以做到这一点,我更愿意监听来自路由器的事件并从那里分派事件,但没有(工作)方式来监听路由器。

import { connect } from 'react-redux';
import React, { Component } from 'react';
import Overview from '../components/Overview';
import { getData } from '../actions';
import { selectOverview } from "../selectors";
import { Redirect } from "react-router-dom";


const defaultDate = "2018-01-02";
const mapStateToProps = (lastProps => (state, ownProps) => {
  var dispatchFetch = false;
  if (lastProps.match.params.day !== ownProps.match.params.day) {
    lastProps.match.params.day = ownProps.match.params.day;
    dispatchFetch = true;
  }
  return {
    ...selectOverview(state),
    routeDay: ownProps.match.params.day,
    dispatchFetch
  }
})({ match: { params: {} } });
const mapDispatchToProps = {
  getData
};
class RedirectWithDefault extends Component {
  render() {
    //I would try to listen to route change and then dispatch getData when
    //  routeDay changes but none of the methods worked
    //  https://github.com/ReactTraining/react-router/issues/3554
    //    onChange in FlexkidsApp.js never gets called and would probably 
    //      result in the same problem of dispatching an event in update cycle
    //    browserHistory does not exist in react-router-dom
    //    this.props.history.listen didn't do anything when trying it in the constructor
    //So how does one dispatch an event when props change in stateless components?
    //  can try to dispatch previous and next day instead of using Link component
    //  but that would not update the url in the location bar
    if (this.props.dispatchFetch) {
      Promise.resolve().then(() => this.props.getData(this.props.routeDay));
    }
    return (this.props.routeDay)//check if url has a date (mapStateToProps would set this)
      ? Overview(this.props)
      : <Redirect//no date, redirect
        to={{
          pathname: "/list/" + defaultDate
        }}
      />
  }
}

export default connect(mapStateToProps, mapDispatchToProps)(RedirectWithDefault);

【问题讨论】:

    标签: reactjs redux react-router


    【解决方案1】:

    最初我希望你的路由器是这样的,

    <Route path="/sessions/:prefDay" component={MyComponent}/>
    

    您必须在 getDerivedStateFromProps() 中执行此操作,

    static getDerivedStateFromProps(nextProps, prevState) {
        if (prevState.prefDay !== nextProps.match.params.prefDay) {
    
         return {
         prefDay: nextProps.match.params.prefDay,
         };
       }
       // Return null to indicate no change to state.
       return null;
      }
    

    【讨论】:

    • 要求是,你想打开一个新的 url 并从那个组件发送调度吗?
    • &lt;Link to={"/sessions/" + prefDay}&gt;{prefDay}&lt;/Link&gt; 告诉 react-router 更新 props 并设置 url,效果很好。但是更新 props 不会导致 componentDidMount 被调用,所以当 react-router 更新 props 时我不知道在哪里调度操作。
    • 好的,现在我明白了,所以您单击前一天的链接,然后通过在同一个组件中显示新数据来渲染同一个组件。好的,根据那个编辑答案
    • 我没有使用状态,而是在无状态组件中使用带有连接的 mapStateToProps,现在有一个错误 Warning: SessionsOverview: Did not properly initialize state during construction. Expected state to be an object, but it was undefined. 因为这是一个静态函数,除非你使用 state 作为,否则无法知道 props 是否改变好吗?
    • 好的,你必须在构造函数中初始化状态才能清除这个错误。你可以这样做,constructor(props){super(props);this.state={prefDay:this.props.match.params.prefDay}}
    【解决方案2】:

    您尝试过 componentWillReceiveProps 吗?

    componentWillReceiveProps(props){
       if(this.props.data != props.data && this.props.state.routeDay){
         this.props.getData(props.state.routeDay);
       }
    }
    

    【讨论】:

    • No I did not 点击链接,您将获得关于在 react 生命周期方法中不调度操作的说明。
    • 啊,好吧,我只是把上面的代码改了一点。你总是希望在这个函数中引用 (props) 变量而不是 this.props 来获取最新的 props。
    • 你不能在 react 生命周期方法中调度动作,因为你会得到警告,你不应该做这样的事情。我正在寻找一种方法来检测 react-router 中的变化,看看我是否可以从那里分派动作,但没有运气。
    • componentDidMount 是一个 react 生命周期方法
    • 我认为他的意思是更新周期
    【解决方案3】:

    要在路由器中添加参数到您需要的路由,请执行此操作

    <Route 
         path="/sessions/:prefDay" 
                    ....
    />  
    

    这意味着现在“/sessions/” rout 有一个名为“prefDay”的参数 示例 /sessions/:prefDay

    现在在Link组件中需要添加这个参数

    <Link to={"/sessions/" + prefDay}>{prefDay}</Link>
    

    你可以像这样从 url 获取这个值

    this.props.match.params.prefDay
    

    【讨论】:

    • 链接有效,它更新了 url,react-router 再次设置了 props,但 componentDidMount 再也不会被调用,因此不会获取新数据。我可以使用 Rohith 的答案并且永远不会更新我的位置或使用您的答案并且在 react-router 设置道具时永远不会获取我的数据。
    【解决方案4】:

    在作为无状态组件容器的 RedirectWithDefaults 中,我添加了 componentDidMount(不是更新周期方法):

    const historyListener = (lastDay => (props,currentDay) => {
        if(lastDay !== currentDay){
            lastDay = currentDay;
            if(!isNaN(new Date(currentDay))){
                console.log("getting:",currentDay);
                props.getData(currentDay);
            }
        }
    })(undefined);
    
    class RedirectWithDefault extends Component {
        componentDidMount(history) {
            this.props.getData(this.props.routeDay || defaultDate);
            this.unListen = this.props.history.listen((location) => {
                historyListener(
                    this.props,
                    location.pathname.split('/').slice(-1)[0]
                );
            });
        }
        componentWillUnmount(){
            this.unListen();
        }
    

    从渲染函数中删除了代码。现在,当它挂载时,它将分派加载数据的操作,当路由更改时,它将再次分派它,但不在更新周期内。

    【讨论】:

      猜你喜欢
      • 2017-07-17
      • 1970-01-01
      • 1970-01-01
      • 2020-04-15
      • 1970-01-01
      • 2017-07-22
      • 2022-01-08
      • 2020-04-24
      • 1970-01-01
      相关资源
      最近更新 更多