Author avatar

Gaurav Singhal

Polymorphism and Action Bubbling in Redux

Gaurav Singhal

  • Oct 18, 2020
  • 6 Min read
  • 35 Views
  • Oct 18, 2020
  • 6 Min read
  • 35 Views
Web Development
Front End Web Development
Client-side Frameworks
React

Introduction

Redux gives a lot of power to React apps by managing their complex state through actions and reducers. It is built on the functional programming methodology where reducers are simple functions that update the state in the Redux store on dispatching actions. However, reducers can be made polymorphic and action bubbling can be mimicked using the Redux global store. Since these concepts aren't original to Redux but an adaptation based on object-oriented programming, they may not be as necessary as in other languages or frameworks. This guide will explain how to achieve polymorphism and action bubbling in Redux.

Polymorphism in Redux

Polymorphism is a technique that allows you to do multiple things using a single form. It's a feature of object-oriented programming through which you can implement function overloading, where a single function can be used to implement different features based on the parameters passed. All the functions have the same name but perform different tasks. Reducers in Redux are also functions inside of which you take action and state as parameters and, on the basis of the action, perform different tasks.

Consider a simple reducer in a React-Redux app that adds, deletes, or updates todos based on the action passed to it.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
export default function todoReducer(state=initState,action){
	switch(action.type){
        case 'ADD_TODO': return {
                                ...state,
                                todos: [...todos,action.payload.newTodo]
                            }
        case 'UPDATE_TODO': return {
                                ...state,
                                todos: todos.map((todo)=>{
                                    if(todo.id===action.payload.updatedTodo.id)
                                    return action.payload.updatedTodo;
                                    else
                                    return todo;
                                })
                            }
        case 'DELETE_TODO': return {
                                ...state,
                                todos: todos.filter(todo=>todo.id!==action.payload.de)
                            }
        default: return state
	}
}
javascript

You have a simple todoReducer that adds a new todo to the list of todos in your state, updates a particular todo, or deletes a particular todo from your state. This is a common implementation of reducers in a React-Redux app. If at any point you want to add a new todo, you can dispatch an action that looks something like this.

1
2
3
4
5
6
7
8
9
10
dispatch({
	type: 'ADD_TODO',
	payload:{
		newTodo:{
			id: 3,
			name: 'Make a new todo list',
			status: 'Pending'
		}
	}
})
javascript

Similarly, to update the above todo, dispatch an action like this.

1
2
3
4
5
6
7
8
9
10
dispatch({
	type: 'UPDATE_TODO',
	payload:{
		newTodo:{
			id: 3,
			name: 'Make a new todo list',
			status: 'Completed'
		}
	}
})
javascript

Thus, actions in the case of a general reducer look like {type: 'DELETE_TODO', payload: {...}} . Using polymorphism, can make this much simpler and more flexible. Instead of having a switch case on every action type in your reducer, you can create a polymorphic reducer that simply calls a handler property on your action and passes the state and payload properties to it. Thus, the above reducer becomes:

1
2
3
export default function todoReducer(state = initState, action) {
    return action.handler(state, action.payload);
}
javascript

And your actions would look like

1
2
3
4
{ 
    handler: ADD_TODO, 
    payload: newTodo:{...}
}
javascript

And then you can create a function that does the required computation based on the handler and returns the result that, in turn, your reducer returns. This makes your reducer polymorphic in the sense that it returns a single instance that does different things based on the handler property attached to it. You can follow the same pattern for any kind of action by attaching the action type inside the handler property.

Action Bubbling in Redux

Actions are dispatched from a component to fire a reducer that modifies or updates the state. Just like events are bubbled from child element to parent element in JavaScript, action bubbling can find a use case when a child component fires an action to modify a state and you need to do the same with the parent component to use that modified state. However, when you break down this use case, it seems that the relevant information is what actually needs to bubble through your component tree in a bottom-up manner rather than the action itself. Consider the following component subtree in a React-Redux app.

1
2
3
4
5
export const ParentComponent =()=>{
	return(
		<ChildComponent/>
	)
}
jsx

The component tree is composed of two components, a ParentComponent and a ChildComponent rendered inside the ParentComponent. If the ChildComponent on dispatching an action can modify the state, ParentComponent can use that modified state directly from your Redux store. It's important to note that the state referred to here is not props passed down to your child component. Dispatching actions to modify state in a global store, in essence, bubbles information through the tree, top-down fashion, bottom-up fashion, and so on. Thus you can implement action bubbling by bubbling information through actions and updating the global store where the modified state can be used by any component in the component tree.

Conclusion

It's important to note that polymorphism is already implemented in Redux reducers to some extent as a single reducer is meant to perform different tasks on the basis of the action passed. The example demonstrated here of creating a polymorphic reducer may not lead to a better approach for small apps having simple actions, and at times it may not be necessary at all. Similarly, as action bubbling is not native to Redux, using the global store to mimic that concept is much better than implementing it from scratch. The ideal implementation might direct away from Redux's own in-built methodology that gives you powerful abilities to write functional, clean, and structured code.

1