4

I am trying to understand Redux and having some difficulty.

I understand the concept of combineReducer, ie ....

var reducer = combineReducers({
    user: userReducer,
    products: productsReducer
})

But what if I have thousands of products, only available on the products page. I do not understand why I need to load them at root; to me this will slow the initial start up of the app for something that will not be needed unless the user goes to the products page.

Is this just the way it is with redux?

3 Answers3

5

In Redux apps, you always build your entire state at the start. With Redux you have one store and one state - everything should trickle down from that one state to props on your components. However, that does not mean you actually need to load all the data into the state at launch, only that the structure needs to be there. This is why you should set up an initial state object for each reducer.

Let's say you have thousands of product records that you load from the database. In your products reducer you could do something like this:

const initialState = {
    data: []
};

//use ES6 default parameters
function productsReducer (state = initialState, action) {
    switch (action.type) {
    case 'GET_PRODUCTS':
        //return data from action
        return {
            data: action.result
        };
    default: 
        return state;
    }
}

This means that when you start your app, if you use the full reducer you declared in your post, your application state will look like this:

{
    user: {},
    products: {
        data: []
    }
}

products.data will be an empty array until you fire an action that actually requires you to load the products data (i.e. you go to the Products page in your app or something). It's true that the products data will remain in your state if you then go elsewhere in your app, but this is a great thing - the next time you render the Products page you will already have the data at your disposal without having to do a database lookup.

Andy Noelker
  • 9,176
  • 5
  • 33
  • 43
  • 2
    Thanks dude, I have not seen it explained as well as you just did. What you show now makes sense to me. I wish there was more real world examples with remote data and less ToDos :(. But thanks! –  Dec 07 '15 at 15:18
  • I should point out that I edited my comment in the reducer slightly because technically you should not do ajax calls in the reducer (although it would generally work). A better flow is to create your ajax call (promise) in your action and then have a bit of middleware that runs it for you and sends that to the reducer. I found this tutorial extremely helpful. Especially look at the src and see the promise-middleware and how its implemented https://github.com/happypoulp/redux-tutorial – Andy Noelker Dec 07 '15 at 15:24
  • @SarasotaSun: Have you looked at the 'real world example' in the `redux` repo? https://github.com/rackt/redux/tree/master/examples/real-world – Jesse Buchanan Dec 07 '15 at 17:26
0

In our app, we made an API for the products and it has limit of 15 per page. So our reducer goes like this.

collection: {
  "total": 0,
  "per_page": 0,
  "current_page": 0,
  "last_page": 0,
  "from": 0,
  "to": 0,
  data: []
},

isFetching: false,
isFetchingError: false

on the first load we fetched limited amount of products, then we made a pagination out of it.. using selectors in redux https://github.com/rackt/reselect

Loading a thousands of data will get your app very slow.

const paginated = (state = initialState, action) => {
 switch (action.type) {
case FETCH_PAGINATED_PRODUCTS:
  return {
    ...state,
    isFetching: true,
    isFetchingError: false
  };

case FETCH_PAGINATED_PRODUCTS_SUCCESS:
  return {
    ...state,
    collection: action.payload,
    isFetching: false
  };

case FETCH_PAGINATED_PRODUCTS_ERROR:
  return {
    ...state,
    isFetching: false,
    isFetchingError: true
  };

default:
 return state

we have used axios for request: https://github.com/mzabriskie/axios

vistajess
  • 663
  • 1
  • 5
  • 21
0

Here's how we implement axios in redux-async

 export function getAll(page = 1) {
  return (dispatch, getState) => {
    const state = getState();
    const { filters } = state.products.paginated;

    if ( state.products.paginated.isFetching ) {
      return;
    }

    dispatch({ type: FETCH_PAGINATED_PRODUCTS });

    return axios
      .get(`products?page=${page}&limit=16&filters=${JSON.stringify(filters)}`)
      .then((res) => dispatch({
        type: FETCH_PAGINATED_PRODUCTS_SUCCESS,
        payload: res.data
      }))
      .catch((res) => dispatch({
        type: FETCH_PAGINATED_PRODUCTS_ERROR,
        /*payload: res.data.error,*/
        error: true
      }));
  }
}

export function get(id) {
  return (dispatch, getState) => {
    const state = getState();

    if ( state.products.resource.isFetching ) {
      return;
    }

    dispatch({ type: FETCH_PRODUCT });

    return axios
      .get(`products/${id}`)
      .then((res) => dispatch({
        type: FETCH_PRODUCT_SUCCESS,
        payload: res.data.data
      }))
      .catch((res) => dispatch({
        type: FETCH_PRODUCT_ERROR,
        /*payload: new Error(res.data.error),*/
        error: true
      }));
  }
vistajess
  • 663
  • 1
  • 5
  • 21