Search code examples
javascriptreduxstore

What's the way to update the whole state with Redux?


I have an empty state object that is instantiated with default empty fields on app start.

It contains an empty array named users.

const state = {
                  users: []
              }

On start I send some commands to server and server then sends data in response.

As I need to populate users, I think I just want to replace the whole users array with a new one that is received from server.

I read that in redux you take the current state, apply some changes to it and return a new state. It sounds good when I want to, say, change user password, then I would call an action like this:

{
    action: 'CHANGE_PASSWORD',
    user: 'john',
    password: 'karamba',
}

It would be managed by the dispatcher, where a user john would be found in the current state and the password would be replaced with the new one.

But is this correct for the scenario where I want to load some initial data?

If so, how would an action look like?

{
    action: 'FETCH_USERS'
}

And then the reducer would replace the whole users array?


Solution

  • Let just cover some basics first

    • All state changes within a app are stored in a single object state tree.
    • This state tree needs to be immutable.
    • Anytime you need to update(replace) the state you will need to dispatch an action, This action event must be captured by a reducer to replace your state.
    • Actions are plane JS Objects describing the minimal change to the state/app. It must have a TYPE property.
    • The UI components don't it self know how the changes are being done. All they know is that they need to dispatch an action.
    • Pure function - Return value depends solely on the value of the arguments, do not have an side effects (network or db calls), idempotent (same arguments always predictability return the same value ), they do not modify the values passed to them
    • Reducer - is a pure function that takes the whole state of the app and the action and returns the next state of the app (ensuring not to modify the current state, it has to return a new object) and has the following method signature: (State,Action) => State;

    So what does your actions look like for Async Flows (where you are retrieving data from network request)? When Async flow you using need to use some type of middleware http://redux.js.org/docs/advanced/Middleware.html.

    In the actions below I have used a middleware called thunk.

    actions.js:

    export function receiveAllProducts(users){
        return {
            type: 'RECEIVED_ALL_USERS',
            users: users
        }
    }
    
    
    export function fetchALLUserData(){
        return thunkLoadAllUserData;
    }
    
    
    const thunkLoadAllUserData = (dispatch) => {
            let users = [];
            users = //Some network request to get data
            dispatch(receivedAllUserData(users));
        });
    }
    

    So you would dispatch an action to fetchALLUserData, which returns a thunk function (redux middleware for Asynchronous Flows) to retrieve the users, which then calls receivedAllUserData action that as a type : 'RECEIVED_ALL_USERS'. This type is is captured by your reducer, which uses a pure function and Object.assign to return a new state (with the new users).

    What does the reducer pure function look like ? reducers.js:

    const initialState = {}
    
    export default (currentState = initialState, action = {}) => {
    
    
      switch (action.type) {
    
        case 'RECEIVED_ALL_USERS':
          return Object.assign({}, currentState, {
            users: action.users,
          });
        case 'RECEIVE_ALL_PRODUCTS':
          return Object.assign({}, currentState, {
            productsAsObject: action.productsObject,
          });
    
        // ...other actions
    
        default:
          return currentState;
      }
    }
    

    The Object.assign() method is used to copy the values of all enumerable own properties from one or more source objects to a target object. And therefore will copy the whole currentState and override your users attributes with the fetched user

    Alternatively you can also use the object spread operator to update the state: http://redux.js.org/docs/recipes/UsingObjectSpreadOperator.html