Using Switch-Cases in React-Redux to Make State Change Dependencies:

David Ryan Morphew
5 min readJul 2, 2021

When Change in One Part of State Requires Change in Another.

You’re using React-Redux. Suppose one part of your state should change if another part changes. Some might call this a supervenience relationship.

FIGURE 7 from “Philosophical Foundations of Neuroscience in Organizational Research: Functional and Nonfunctional Approaches”.

Whatever you call it—you want a change in X part of state to always result in a change in Y part of state. This article shows you one solution: set two parts of state to respond to the same action type with a switch-case.

I’ll give you two examples: one, with a single reducer (Example 1), and another, which I used in my latest React app, which uses combineReducers (Example 2).

Example 1: Change in Values => Change in Average

Say you have one part of state in your Redux store that tracks children and their respective heights in inches (childrenHeights) and adults and their respective heights in inches (adultHeights) like so:

state = {
childrenHeights: [
{childName: "Alfalfa", heightInInches: 40},
{childName: "Darla", heightInInches: 44},
{childName: "Spanky", heightInInches: 42}
],
adultsHeights: [
{adultName: "Larry", heightInInches: 60}
{adultName: "Curly", heightInInches: 62}
{adultName: "Mo", heightInInches: 58}
],
...
}

Now suppose that you have another part of state that just stores the average heights of children, adults, and all individuals in inches (averageHeights):

state = {
childrenHeights: [
{childName: "Alfalfa", heightInInches: 40},
{childName: "Darla", heightInInches: 44},
{childName: "Spanky", heightInInches: 42}
],
adultsHeights: [
{adultName: "Larry", heightInInches: 60}
{adultName: "Curly", heightInInches: 62}
{adultName: "Mo", heightInInches: 58}
],
averageHeights: {
adultHeightAverage: 60,
childHeightAverage: 42,
allHeightAverage: 51
}
}

Now, if you change the state of childrenHeights or adultsHeights, you should change the state of your averages if, in fact, any of the average values need to change.

Not your average three adults.

Solution

if you need to make sure a method gets fired when you add, remove, or change a height value, you can have a switch-case in your reducer for the averageHeights to respond to changes in the childrenHeights or adultsHeights state. Take the following case in which a new child and their height is added:

heightsAveragesReducer = (state = { childrenHeights: [],  
adultsHeights: [], averageHeights: {} }, action) => {
switch (action.type){
case 'ADD_CHILD':
const newChildWithHeight = action.payload
const newChidrenHeightsState = [...state.childrenHeights,
newChildWithHeight]
const newChildHeightAvg =
newChildrenHeightsState.customAveragerOfHeightValuesFn()
const allUpdatedPeopleHeights =
adultsHeights.concat(newChildrenHeightsState)
const newOverallAvg =
allUpdatedPeopleHeights.customAveragerOfHeightValuesFn()

return {...state, childrenHeights: newChildrenHeightsState,
averageHeights: {...averageHeights,
childHeightAverage: newChildHeightAvg,
allHeightAverage: newOverallAvg}
}
default:
return state
}
}

Let’s break it down:

  1. newChildwithHeight comes in as the action.payload, and we add it to our old childrenHeightsState, calling this new part of state newChildrenHeightsState:
const newChildWithHeight = action.payload
const newChidrenHeightsState = [...state.childrenHeights,
newChildWithHeight]

2. Adding a child and their height now forces us to recalculate the average height of all of the children:

const newChildHeightAvg = 
newChildrenHeightsState.customAveragerOfHeightValuesFn()

Here, I’ve just conjured up a function that takes the values of all of the heights and calculates the average (customAveragerOfHeightValuesFn).

3. But we also need to recalculate the average of everyone, the allHeightAverage. To do that, take heights of all of the adults, the new list of children and their heights, and average that:

const allUpdatedPeopleHeights = 
adultsHeights.concat(newChildrenHeightsState)
const newOverallAvg =
allUpdatedPeopleHeights.customAveragerOfHeightValuesFn()

4. Finally, return the updated state, merging the new values in a new copy of state using the spread operator:

return {...state, childrenHeights: newChildrenHeightsState, 
averageHeights: {...averageHeights,
childHeightAverage: newChildHeightAvg,
allHeightAverage: newOverallAvg}
}

All we’ve done here is make sure that a change in one part of state (child and their height) also results in the change of another part of state (the average height of children and the average height of children and adults). We put all of the changes of state into one switch-case for when a new child is added.

Example 2: Change in Owned Values => Change in Potential Values

In building out my latest React app, called MyBricksApp, I wanted one part of my store state (in the Redux store) to change when another was changed.

A state tree of my React-Redux App called MyBricks
What tangled webs we weave…

Starting from the beginning, my React app lets you keep track of Lego sets that you own. Each Lego set is stored as an object in an array in the store under the key of legoSets:

legoSets: [
{id: 751,
name: "Tractor",
setNumber: "6608-1",
imageUrl: "https://cdn.rebrickable.com/media/sets/6608-1/9875.jpg",
instructionsUrl: "https://rebrickable.com/instructions/6608-1",
year: 1982,
owned: true,
totalBricks: 21,
themeName:"Farm"},
...
]

You can toggle whether you own or don’t own any set in the app and change the owned boolean property.

Based on the Lego sets that you own, I built a search feature in my Rails Api that finds what other Lego sets you could build based on all of the parts you own. I stored these Lego sets results in an array in the Redux store under the key of potentialBuilds once the search is complete:

potentialBuilds: [
{id: 74,
name: "Bandit Ambush",
setNumber: "6024-1",
imageUrl: "https://cdn.rebrickable.com/media/sets/6024-1/3229.jpg",
instructionsUrl: "https://rebrickable.com/instructions/6024-1",
year: 1996,
owned: false,
totalBricks: 60,
themeName: "Dark Forest"},
...
]

To make a long story short, the search feature currently takes around 21–23 seconds if you own about 30 moderately sized Lego sets. So, it is not a simple task to just set the search feature to run every time that you add or remove a Lego set from your owned list.

Instead, you have to navigate to the “Potential Builds” tab in the NavBar and click a button to search for sets you can build with what you own. Then wait.

Screenshot of my App’s NavBar and Potential Builds client-side routed page with selection buttons to search for sets you can build with what you already own.

Outdated Data?

But then that leads to a potential problem. After the Redux store’s state is updated, what if you add or remove Lego sets that you own? Won’t the search results for potential builds need to change and be recalculated?

To avoid having outdated potential builds data when you change the sets that you own, I set up a switch-case to clear the potentialBuilds part of the Redux store’s state if you change any of the sets that you own:

const potentialBuildReducer = (state = [], action) => {
switch (action.type){
...
case 'TOGGLED_OWNED_VALUE':
return []
...
}
}

So now, if the state of any Lego set’s owned value is changed, like so:

const legoSetReducer = (state = [], action) => {
switch (action.type){
...
case 'TOGGLED_OWNED_VALUE':
const setForUpdate = state.find(set => set.id ===
action.payload.id)
const updatedSet = {...setForUpdate, owned:
action.payload.owned}
const setForUpdateIndex = state.indexOf(setForUpdate)
return [...state.slice(0, setForUpdateIndex), updatedSet,
...state.slice(setForUpdateIndex + 1)]
...
}
}

…then the potentialBuilds state is reset and will require you to use the search feature again.

This lets you split up parts of state with combineReducers:

const rootReducer = combineReducers({
legoSets: legoSetReducer,
...
potentialBuilds: potentialBuildReducer
})

while still having different parts of state to be responsive to the same action types, setting a up a switch-case for the same action type in your two reducers.

--

--

David Ryan Morphew

I’m very excited to start a new career in Software Engineering. I love the languages, frameworks, and libraries I’ve already learned / worked with (Ruby, Rails,