Phát Nguyễn
Phát Nguyễn

Reputation: 185

reselect object from redux

I using reselect and I have some problem about select state from my store.

My redux store look like:

store = {
  boxs: {
    space: 'large',
    view: 'card',
    options: {
      option01: { id: '01', name: 'Option1', value: true },
      option02: { id: '02', name: 'Option2', value: false },
      option03: { id: '02', name: 'Option3', value: false },
    },
  },
};

Here is my selector.js

const getSpace = createSelector(
  (state) => state.boxs.space,
  (space) => space
);

const getSpaceAndView = createSelector(
  (state) => state.boxs,
  (boxs) => ({
    space: boxs.space,
    view: boxs.view,
  })
);

const getOptions = createSelector(
  (state) => state.boxs,
  (boxs) => ({ ...boxs.options })
);

// I use this but when options change value, Component3 not re-render, why ???
/*const getPreferences = createSelector(
  (state) => state.boxs,
  (boxs) => boxs.options
);*/

My reducer

reducers = (state = initState, action) => {
  switch (action.type) {
    ...
    case 'CHANGE_OPTIONS':
      const { key, val } = action; // key = 'option01' | 'option02' | 'option03', value = true || false
      const newState = { ...state };
      newState.boxs.options[key].value = val;
      return newState;
    default:
      return state;
  }
}

I have 3 components. Component1 use state.boxs.space. Component2 use state.boxs.view and state.boxs.space. Component3 use state.boxs.options. When Component3 change state.boxs.options value (dispatch action CHANGE_OPTIONS in my reducer). Component2 and Component1 will re-render. How to stop re-render ? Thank for your help.

Upvotes: 0

Views: 308

Answers (1)

Drew Reese
Drew Reese

Reputation: 202618

I use this but when options change value, Component3 not re-render, why ???

/*const getPreferences = createSelector(
  (state) => state.boxs,
  (boxs) => boxs.options
);*/

The reducer should be a pure function. You look to be mutating state objects. You should shallow copy all the data you'll be updating.

reducers = (state = initState, action) => {
  switch (action.type) {
    ...
    case 'CHANGE_OPTIONS':
      const { key, val } = action; // key = 'option01' | 'option02' | 'option03', value = true || false

      return {
        ...state,
        boxs: {
          ...state.boxs,
          options: {
            ...state.boxs.options,
            [key]: {
              ...state.boxs.options[key],
              value: val,
            },
          },
        },
      };

    default:
      return state;
  }
}

A small improvement to be more DRY and utilize memoization is to create an initial input selector to select your state.boxs object to be used as input to the other selectors.

const boxsSelector = state => state.boxs;

const getSpace = createSelector(
  [boxsSelector],
  boxs => boxs.space,
);

const getSpaceAndView = createSelector(
  [boxsSelector],
  boxs => ({
    space: boxs.space,
    view: boxs.view,
  })
);

const getOptions = createSelector(
  [boxsSelector],
  boxs => boxs.options,
);

Upvotes: 1

Related Questions