Right way to update state in redux reducers

Viewed 95457

I'm a newbie in redux and es6 syntax. I make my app with official redux tutorial, and with this example.

There is JS snippet below. My point - to define REQUEST_POST_BODY and RECEIVE_POST_BODY cases in posts reducer. Main difficult - to find and update right object in store.

I try to use code from example:

  return Object.assign({}, state, {
    [action.subreddit]: posts(state[action.subreddit], action)
  })

But it used simple array of posts. It's not needed to find right post by id.

Here my code:

  const initialState = {
    items: [{id:3, title: '1984', isFetching:false}, {id:6, title: 'Mouse', isFetching:false}]
  }

  // Reducer for posts store
  export default function posts(state = initialState, action) {
    switch (action.type) {
    case REQUEST_POST_BODY:
      // here I need to set post.isFetching => true
    case RECEIVE_POST_BODY:
      // here I need to set post.isFetching => false and post.body => action.body
    default:
      return state;
    }
  }

  function requestPostBody(id) {
    return {
      type: REQUEST_POST_BODY,
      id
    };
  }

  function receivePostBody(id, body_from_server) {
    return {
      type: RECEIVE_POST_BODY,
      id,
      body: body_from_server
    };
  }

  dispatch(requestPostBody(3));
  dispatch(receivePostBody(3, {id:3, body: 'blablabla'}));
3 Answers

I pretty much implemented Object reducers by using Object.assign, which works, but as our project has grown and we have added a bunch of dependent components it has become very inefficient and renders are very slow.

If I'd know about immer I would have used that from the start.

Essentially you use immer as follows, where the example has a layers object that looks like this:

const initialState = {
  layers: {
   'layer1': { selected: false },
   'layer2': { selected: true }
  }
}

Reducers.js (extract)

import produce from 'immer'
import has from 'lodash/has'
export const layers = (state = null, action) => {
  switch (action.type) {
    case ADD_LAYER:
      // Using `immer.produce` only for consistency 
      // clearly the whole object changes in this case.
      return produce(state, layers => {
        // Take a copy of the prebuilt layer
        var layer = Object.assign({}, action.layer)
        // Add some defaults
        if (!has(layer, 'selected')) {
          layer.selected = false
        }
        layers[action.id] = layer
      })
    case SELECT_LAYER:
      return produce(state, layers => {
        layers[action.id].selected = true
      })
    case UNSELECT_LAYER:
      return produce(state, layers => {
        layers[action.id].selected = false
      })
    default:
      return state
  }
}

Actions.js (extract)

export const addLayer = id => ({
  type: ADD_LAYER,
  id
})

export const selectLayer = id => ({
  type: SELECT_LAYER,
  id
})

export const unSelectLayer = id => ({
  type: UNSELECT_LAYER,
  id
})

References:

https://github.com/immerjs/immer

https://redux.js.org/recipes/structuring-reducers/immutable-update-patterns

Related