【问题标题】:dispatching actions on every render在每个渲染上调度动作
【发布时间】:2021-03-01 14:15:15
【问题描述】:

我在 ProductList 组件的每个挂载上调度动作 addProducts 而我想使用 useEffect 钩子调度动作一次并将数据存储在 redux 中然后使用它。

以下是我的操作文件和 ProductList 组件文件。

actions.js 文件

export const addProducts = () => async (dispatch) => {
  let Products = await axios.get("https://api.npoint.io/2a4561b816e5b6d00894");
  return dispatch({
    type: ADD_PRODUCTS,
    payload: Products.data,
  });
};

ProductList.js 组件文件

import { addProducts } from "../actions/Index";
const ProductList = () => {
  const dispatch = useDispatch();
  useEffect(() => {
   dispatch(addProducts()); 
  },[]);
  const Products = useSelector((state) => state.products);
  console.log(Products)

【问题讨论】:

    标签: javascript reactjs redux use-effect


    【解决方案1】:

    您可以只在组件中调度操作,但如果产品可用,则在 thunk 操作中不执行任何操作:

    export const addProducts = () => async (
      dispatch,
      getState,//thunk also get a getState function
    ) => {
      //you should write a dedicated selector function so you could do:
      //  const productsInState = selectProducts(getState())
      const productsInState = getState().products
      //whatever would make state.products available
      //  reducers are missing in your question
      if(productsInState){
        //do nothing if products are already in state
        return;
      }
      let Products = await axios.get(
        'https://api.npoint.io/2a4561b816e5b6d00894',
      );
      return dispatch({
        type: ADD_PRODUCTS,
        payload: Products.data,
      });
    };
    

    在您的组件中,您可以只在每个渲染上分派,如果您的页面有多个分派此操作的组件,那么您可以创建一个grouped action

    【讨论】:

    • 您不知道axios-api-call 需要多长时间...在此期间products 的状态将是null ...因此它们可能是多个组件触发相同的操作导致多次调用axios.get .. ...
    • @HendEl-Sahli 答案中提到的广告; group the action 如果可以在一个渲染周期内调度。
    【解决方案2】:

    您想使用if(!products){...} 来检查产品是否已在 redux 中,例如

    const addProducts = () => async (dispatch) => {
      let Products = await axios.get("https://api.npoint.io/2a4561b816e5b6d00894");
      return dispatch({
        type: ADD_PRODUCTS,
        payload: Products.data,
      });
    };
    
    const ProductList = () => {
      const dispatch = useDispatch();
      const products = useSelector((state) => state.products);
      useEffect(() => {
       if (!products) {
         dispatch(addProducts()); 
       }
      },[dispatch]);
      return <p>foo</p>
    }
    

    【讨论】:

    • 为什么要用逻辑来负担组件,这应该在行动中。
    • > 这应该在行动中。我认为这并不重要,它可以,但我不会做到应该。无论哪种方式,它都回答了用户的问题。
    • 那你就不懂redux了。一个动作表明在这种情况下发生了某些事情component needs data,如果该动作从缓存中获取或获取不是您应该放入组件中的东西。如果您确实需要这样做,请将cache=true 或其他内容传递给动作创建者。如果您稍后更改缓存的工作方式,您需要更改所有调度该操作的代码,而不是只更改一个操作。
    • 我喜欢你的热情,我明白你的意思,但在回答用户问题方面并没有太大变化。
    • 答案也有 Hend 指出的问题,如果一个渲染周期多次调度该动作,您仍然会多次获取。所以现在你有多个组件需要一些额外的逻辑,效果是 all 需要改变你明白我想说的意思吗?工作并不意味着容易维护。
    【解决方案3】:
    export const addProducts = () => async dispatch => {
      dispatch({
        type: ADD_PRODUCTS_START,
        payload: { loading: true },
      });
    
      const Products = await axios.get(
        'https://api.npoint.io/2a4561b816e5b6d00894'
      );
    
      dispatch({
        type: ADD_PRODUCTS_SUCCESS,
        payload: { products: Products.data, loading: false },
      });
    };
    
    const ProductList = ({ products, loading }) => {
      useEffect(() => {
        if (!products && !loading) {
          dispatch(addProducts());
        }
      }, []);
    };
    
    
    const mapStateToProps = ({ products: { data, loading } }) => ({ products: data, loading });
    

    【讨论】:

    • 你不应该在组件中添加这个逻辑,组件应该只是调度动作,动作可以决定它是否要获取产品。如果您希望您的组件指示它是否需要新数据,您最多可以将 cache=true 传递给操作创建者。
    • @HMR 我必须承认......你的回答很直接......而且很干净!
    猜你喜欢
    • 2012-03-09
    • 1970-01-01
    • 2011-11-07
    • 2018-09-28
    • 1970-01-01
    • 1970-01-01
    • 2017-09-28
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多