2

I am dispatching action addProducts on every mount of the ProductList component whereas i want to dispatch the action one timeusing useEffect hook and store the data in the redux and then use it.

Below are my actions file and ProductList component file.

actions.js file

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 component file

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

3 Answers 3

2

You could just dispatch the action in the component but in the thunk action do nothing if products are available:

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,
  });
};

In your component you can just dispatch on each render, if your page has multiple components dispatching this action then you could make a grouped action.

Sign up to request clarification or add additional context in comments.

2 Comments

You don't know how long the axios-api-call would take ... during that time products in state will be null ... so their could be multiple components firing the same action causing multiple calls for axios.get .. ...
@HendEl-Sahli Ad mentioned in the answer; group the action if it can be dispatched within one render cycle.
2

You want to check if products are already in redux with an if(!products){...} e.g.

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>
}

5 Comments

Why burdon the the component with the logic, this should be in the action.
> this should be in the action. I don't think it really matters much, it could but I'm wouldn't go as far as should. Either way it answers the users question.
Then you don't understand redux. An action indicates something happened in this case component needs data if the action would fetch or get from cache is not something you should put in the component. If you do need to do this then pass cache=true or something to the action creator. If you change the way your cache works at a later time you need to change all code that dispatches the action instead of just change the one action.
Well I like your enthusiasm and I see your point, but it really doesn't change much in terms of answering the users question.
The answer also has the problem Hend pointed out, if one render cycle dispatches that action multiple times you still fetch multiple times. So now you have multiple components that need some extra logic in the effect that all need to change do you see the point I'm trying to make? Working does not mean it is easy to maintain.
1
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 });

2 Comments

You should not add this logic in the component, component should just dispatch the action, the action can decide if it wants to fetch products or not. At best you can pass cache=true to the action creator if you want your component to indicate it needs fresh data or not.
@HMR I must admit ... your answer is alot straight-farward .. and clean!

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.