【问题标题】:Asynchronous ActionCreator in React ReduxReact Redux 中的异步 ActionCreator
【发布时间】: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,因此请使用选择器的强大功能!只需在您需要时分派操作,然后让选择器将其作为组件的道具传递...然后您的渲染器将完成其余的工作,同时它是异步调用,在您的渲染器中显示一些加载反馈。那是你应该遵循的模式,请解释一下你为什么要打破这种模式。因为问题就在那里……

标签: javascript reactjs redux react-redux redux-thunk


【解决方案1】:

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

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

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

  3. 当您的 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
    
        });
    });
    

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

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2016-09-22
      • 2016-05-01
      • 2019-01-20
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2022-01-16
      • 1970-01-01
      相关资源
      最近更新 更多