0

I have the following normalized redux state:

rootReducer: {
  blocks: {
    "key1": {
      id: "key1",
      beverages: [], // Array of objects
    }
  }
}

and I'm trying to select the value of beverages for beverage with the id of "key1" using this selector:

export const getBlockBeverages = (state, blockId) => {
    console.log("selector", state.blocks[blockId].beverages);
    return state.blocks[blockId].beverages;
};

Whenever I add a new beverage into the beverages array, the selector gets called twice, first time with an empty array, second time with proper values:

Initial state

selector []
selector []

Adding new beverage:

selector []
selector [{/*beverage1*/}]

// Adding another beverage
selector []
selector [{/*beverage1*/}, {/*beverage2*/}]

I'd really appreciate any help/explanation why does the selector get called and beverages value for the block instance is an empty array.

Below is the code for reducers I'm using - I don't see where I could be mutating the original state, I used Immer's produce from the beginning and the problem is still present. Then I tried to use lodash.clonedeep to make sure that I return a new state, but the selector still logs that empty array.

const blockReducer = (state = { id: "", beverages: [] }, action) => {
    if (action.type === ADD_BEVERAGE_TO_BLOCK) {
        const { beverageId } = action.payload;
        const newBeverage = { id: uuid4(), beverageId };
        return produce(state, (draft) => {
            draft.beverages.push(newBeverage);
        });
    }
    return state;
};


const blocks = (state = {}, action) => {
    const key = action.payload.key;
    if (key && (state[key] || action.type === CREATE_BLOCK)) {
        const instanceState = blockReducer(state[key], action);
        return produce(state, (draft: any) => {
            draft[key] = instanceState;
        });
    }
    return state;
};

Any ideas why the selector returns empty array instead of array of length 0, 1, 2 etc. as I'm adding new beverages? I'm clueless and will appreciate any help.

mtbno
  • 561
  • 7
  • 12

1 Answers1

0

The problem was in a different selector that I had been using in a wrong way.

export const makeGetBlockBeveragesLength = () => createSelector(
    (state, blockId) => getBlockBeverages(state, blockId),
    (blockBeverages) => blockBeverages.length,
);

and instead of mapStateToProps I used createMapStateToProps:

const createMapStateToProps = (state, { blockId }) => () => {
    const getBlockBeveragesLength = makeGetBlockBeveragesLength();
    return {
        length: getBlockBeveragesLength(state, blockId),
    };
};

export const Component = connect(createMapStateToProps)(MyComponent);

The empty array logged in one of the logs refers to an older state (the initial state in this case).

I fixed the code to this and it works:

export const getBlockBeveragesLength = createSelector(
    (state, blockId) => getBlockBeverages(state, blockId),
    (blockBeverages) => blockBeverages.length,
);
const mapStateToProps = (state, { blockId }) => ({
    length: getBlockBeveragesLength(state, blockId),
});

export const Component = connect(mapStateToProps)(MyComponent);
mtbno
  • 561
  • 7
  • 12