React Redux 中的异步 ActionCreator

Posted

技术标签:

【中文标题】React Redux 中的异步 ActionCreator【英文标题】:Asynchronous ActionCreator in React Redux 【发布时间】:2019-01-14 08:30:00 【问题描述】:

我是 React-Redux 的新手。正在开发一个应用程序。问题是我在异步执行 Redux actionCreator 时遇到了一些问题,可能是这样。

下面是我的组件。比如说,我想从 componentDidMount() 或从 onclick 事件侦听器调用 actionCreator。

class Dashboard extends PureComponent 

     componentDidMount() 

          this.props.getProductsAndPackages();

          let something = [];
          something = this.props.products;

     
....................................

或者,this.props.getProductsAndPackages(); 函数可以是一个 onClick 事件处理程序,它做同样的事情,上下文是相同的。在先解释我的代码后,我会问我的问题。

在我的仪表板容器的下侧:

Dashboard.propTypes = 
getProductsAndPackages: PropTypes.func.isRequired,
products: PropTypes.array.isRequired,
.......................

 ;

const mapStateToProps = (state) => 
    return 
         .....................
         products: state.products.products,
         ...................
    ;  
;


const mapDispatchToProps = (dispatch) => 
    return 
        getProductsAndPackages: () => dispatch(getProductsAndPackagesActionCreator()),
    ;
;

export default withRouter(connect(mapStateToProps, mapDispatchToProps)(Dashboard));

我的 actionCreator 是这样的:

export const getProductsAndPackagesActionCreator = () => 

return (dispatch) => 

    dispatch(productsIsLoading(true));

    let url = 'xyz';

    if(!!localStorage.getItem('_token')) 
        const local_token = localStorage.getItem('_token');
        const fullToken = 'Bearer '.concat(local_token);

        axios.get(url, headers: Authorization: fullToken)
            .then(response => 
                dispatch(productsIsLoading(false));
                if (response.data.statusCode === 200) 
                    dispatch(productsFetched(true));
                    dispatch(products(response.data.data));
                 else 
                    dispatch(productsFetched(false));
                    dispatch(productsErrors(response.data.message));
                

            )
            .catch(error => 


            );

     else 

        axios.get(url)
            .then(response => 
                dispatch(productsIsLoading(false));
                if (response.data.statusCode === 200) 
                    dispatch(productsFetched(true));
                    dispatch(products(response.data.data));
                 else 
                    dispatch(productsFetched(false));
                    dispatch(productsErrors(response.data.message));
                

            )
            .catch(error => 
                console.log(error);
                dispatch(productsIsLoading(false));
                dispatch(productsErrors(error.message));

            );

    


;
;

现在,我希望我的 getProductsAndPackagesActionCreator() 返回一个 Promise 或任何允许我的 something 变量获取从服务器返回的实际数据的东西。现在,当我得到实际数据时,something=this.props.products 行已经被执行,我取回了为products 设置的 initialValue。

我知道,每当我收到填充的 products 时,组件都会重新渲染,但这无助于我的决策。

顺便说一句,我正在使用redux-thunk

我现在该怎么办?抱歉发了这么长的帖子。

【问题讨论】:

您好,您可以将您的回复数据传递给您的操作payload,然后更新您的商店。完成此操作后,您将不再需要任何 cont something,因为它将在您的组件中以 props 的形式从 mapStateToProps 获得 我已经完成了。如您所见,我只是将 this.props.products 值分配给 something。然而问题是,当 axios 调用结束时,something=this.props.products 已经被执行了。并且 something 被分配了主要从我的减速器设置的 initialState 值。我想确保,我的 something 变量仅分配有从 axios get 请求接收到的填充数组。 顺便说一句:可能你有错误:dispatch(productsIsLoading(false)); 应该在 finally 块中,因为并不总是调用 catch。 最初我在 catch 块中有该代码。但为了简洁起见,我省略了它。 第一个问题是:你到底为什么要这个?您正在使用 React 和 Redux,因此请使用选择器的强大功能!只需在您需要时分派操作,然后让选择器将其作为组件的道具传递...然后您的渲染器将完成其余的工作,同时它是异步调用,在您的渲染器中显示一些加载反馈。那是你应该遵循的模式,请解释一下你为什么要打破这种模式。因为问题就在那里…… 【参考方案1】:

我认为你已经接近得到你想要的了。首先,您应该了解 redux 操作和像 setState 这样的 react 操作是异步的,因此您必须牢记这一点来应用您的逻辑。我将在某些方面解释我的想法:

    您已经在正确的位置调用了动作创建者componentDidMount,如果需要,您也可以在任何 onClick 中调用此动作。

    我想,一旦您发送操作,您正在更改您的 redux 状态设置 loading true。所以现在你可以在你的渲染函数中访问这个属性,这样你就可以渲染一个加载器,直到你的 api 调用完成。

    当您的 ajax 函数完成时,无论是否出错,我想您将 loading 设置为 false 并更新您的产品数据,因此您现在可以在仪表板中呈现您加载的产品。

    李>

您确定必须将空产品数组与接收到的数据进行比较吗?也许你可以检查你的渲染函数if (!this.props.products.length) return null,当你加载你的页面时,你会看到一个加载器函数,然后你会看到带有产品的仪表板。

如果您确实需要将以前的产品与收到的产品进行比较,componentDidUpdate 是您的方法。在这个方法中,你可以访问你之前的 props 并与实际的 props 进行比较,小心比较数组,记住 [] === [] 是 false。也许你可以比较一下长度,比如

componentDidUpdate(prevProps)
   if(prevProps.products.length !=== this.props.products.lenth)          
       doSomething()
   

只是说 componentDidUpdate 在渲染之后执行,所以要小心你的代码,不要执行额外的渲染。

希望对你有帮助,如果你不明白,请告诉我:)

【讨论】:

【参考方案2】:

实际上,我希望getProductsAndPackagesActionCreator() 返回一个承诺,说实话,这非常简单。我发现如果你只是 return axios.get()axios.post(),它将返回一个承诺。因此,修改后的代码如下所示:

export const getProductsAndPackagesActionCreator = () => 

  return (dispatch) => 

    dispatch(productsIsLoading(true));

    let url = 'xyz';

    if(!!localStorage.getItem('_token')) 

        return axios.get(url, headers: Authorization: fullToken)
            .then(response => 
               ............
               ............
            )
            .catch(error => 


            );

     else 

        return axios.get(url)
            .then(response => 
                ...........
                ...........
            )
            .catch(error => 
                console.log(error);
            );
    
  ;
;

然后,我可以在componentDidMount() 或任何 onClick 事件中执行以下操作:

this.props.getProductsAndPackages().then(() => 
    this.setState(
        ...this.state,
        clicked_product: this.props.product_by_id
    , () => 

       //do other stuffs

    );
);

如果有任何问题,请随时告诉我。

【讨论】:

以上是关于React Redux 中的异步 ActionCreator的主要内容,如果未能解决你的问题,请参考以下文章

无法在 react-redux 中异步发布

异步 API 调用,Redux React-Native

如何解决 react-redux 中的此错误“错误:操作必须是普通对象。使用自定义中间件进行异步操作。”

React中redux的异步性问题?

React + Redux 处理异步请求

Redux 和 isFetching 中的顺序异步操作