Redux and Context API with React Native App: Introduction, Use Cases, Implementation, and Comparison www.bacancytechnology.com
Developer 1 and Developer 2 were fighting over which state management tool is better- Redux or Context API. They both listed pros and cons but sadly none realized the actual use case and purpose of Redux and Context API.
If you’re one of the developer 1 or 2, then continue reading the tutorial to learn why it shouldn’t be Redux vs Context API.
Introduction
If you’re having a Javascript background then you might be familiar with the terms Redux and Context API. And probably, you might have come across so many blogs that debate on which is better- Redux vs Context API. I assumed the same unless I realized it’s not!
After reading a bunch of blogs, I concluded the purpose of both the tools and how different they are from each other. If you aren’t aware yet, don’t worry this tutorial will help you understand the use cases of both tools with a basic demo example. Here, we will build an app using both approaches and discuss them.
Let’s get started, then!
Tutorial Goal
Understanding Redux and Context API Comparing the working of Redux and Context API Exploring the purpose and use case of Redux and Context API A demo application using Redux and Context API approach
Redux: Introduction and Building Blocks
According to documentation
Redux is a pattern and library for managing and updating application state, using events called “actions”. It serves as a centralized store for state that needs to be used across your entire application, with rules ensuring that the state can only be updated in a predictable fashion.
The documentation clearly mentions that redux is for “managing state” and understanding how the state is updated.
Use Cases of Redux The primary goal of redux, as mentioned in the doc, is to manage and keep track of the state. Keeping your state management logic separate from the user interface layer Faster logic debugging
Redux is mainly used to manage the state of React applications in a centralized place where to access the state anywhere in the application. Technically, The concept of Redux is based on Flux architecture and this concept isn’t restricted to React apps; there are implementations in different technologies, as well (e.g. NgRx for Angular). But Redux is particularly implemented with React.
Packages needed redux: For the functions like createStore(), combineReducer() etc. react-redux: For the functions like connect() etc.
Building Blocks of Redux It consists of mainly four building blocks:
1. Reducer: These are functions with state and actions passed in as arguments. It contains “action.type” in switch cases which returns the changed value. It optionally accepts the payload (generally created in a separate file known as reducers.js)
2. Store: Store is the collection of all data. You can pass it to the provider.
3. Provider: A React component that accepts store as an argument (usually created in index.js)
4. Actions: Functions that provide/return action type and payload to the dispatcher which will further call the respective reducer (generally created in a separate file known as actions.js)
Context API: Introduction and Building Blocks
React documentation explains context API as
Context provides a way to pass data through the component tree without having to pass props down manually at every level.
In a typical React application, data is passed top-down (parent to child) via props, but such usage can be cumbersome for certain types of props (e.g. locale preference, UI theme) that are required by many components within an application. Context provides a way to share values like these between components without having to explicitly pass a prop through every level of the tree.
If you can observe the documentation states context for “passing” and “sharing” values and mention nothing about “managing the state”
Use Cases of Context API The main purpose of using context API is avoiding ‘prop drilling’ – passing prop at every level. It is more like a pipeline used to pass values from one end to another.
Context API provides the easiest way for passing data through the component tree so that you don’t have to pass props down manually at every level. For example, assume we have a component tree consisting of A, B, C, and D components. Now you need to pass props from A to D, so rather than passing it A > B > C > D,i.e., to every component, with the help of context you can directly pass to A > D.
Now, many blogs have mentioned that Context API is the best replacement for Redux because it’s in-built and you don’t have to install dependencies for the same. Is this really trueCan Context API replace Redux? We will discuss this in a further section. Stay tuned to explore!
Building Blocks of Context API We can divide the Context API into three blocks:
1. Context: Use createContext() function that takes the default value as a first argument. Here it is optional to pass a Javascript object. You can implement multiple contexts in your app.
2. Provider: After creating context, the Provider provides the capability for accessing the context. It provides functions & data to pass values further to the component.
3. Consumer: Consumer allows access to the value to child components which are wrapped by Provider. It has two types
Context.Consumer: Context.Consumer can be used for both functional and class-based components. However, through this approach, the context is accessible within the render method only. Static ContextType: Static contextType can be used only for Class-based components.
Redux and Context API Example
The example below is based on a Counter. The initial value will be 0 and it has two buttons to increment and decrement the value. Inside the main parent counter component, there will be three child componentsone for changing the counter value two for each of the buttons.
The initial setup would be the same for both Context and Redux approaches.
Create a React Native App Initially, create a react native app using the following command react-native init CounterDemo
Redux Approach: How to Implement Redux in React Native App?
Install Required dependencies for Redux
npm install redux --save npm install react-redux --save
Redux Store Set Up We will create our store within our App.js file.
// App.js import React, { Component } from 'react'; import { Provider } from 'react-redux'; import { createStore } from 'redux'; import totalReducers from './reducers/index.js'; import Counter from './components/counters'; const store = createStore(totalReducers); export default class App extends Component{ render(){ return( ); } }
Here we are importing totalReducers from the reducers folder. createStore() function accepts one argument as totalReducers object and generates the store. The Provider component makes sure the store is available throughout the application.
Reducers Set Up Reducers return the data required by the application. In this demo, the reducer will return the updated counter value. Here is our counterReducer.js file inside the reducers folder.
// reducers/counterReducer.js let count= 0; export default (state = count , action) => { switch (action.type) { case "Increment": count ++ break; case "Decrement": count -break; default: count; } return count; }
Explanation The reducer defined above will always return the count value. Increment & Decrement are the actions types that will update the value as shown above. We will combine all the reducers inside the index.js file inside the reducers folder. // reducers/index.js import {combineReducers} from 'redux'; import counterReducer from './counterReducer'; const totalReducers= combineReducers({ count: counterReducer, }); export default totalReducers;
Explanation
Here we will combine all the reducers as arguments of the combineReducers() function of the Redux library.
Actions Set Up Create two actions: Increment & Decrement.
// actions/index.js
export function increment(){ return{ type: "Increment" }; } export function decrement(){ return{ type: "Decrement" }; }
UI Component We will simply create only one component called a counter component. For using reducers and actions, we have to implement these functions:
mapStateToProps() – It simply accepts your reducer data, and converts it into a simple usable prop. With the help of this.props.data we will use data as a prop in our component. function mapStateToProps(state){ return{ count: state.count }; }
Note: Keep in mind how we assigned names to reducers in the combineReducers function because we have to use the same name for calling respective reducers.
mapDispatchToProps() – It accepts your actions, and converts them into a simple usable prop. function mapDispatchToProps(dispatch){ return bindActionCreators({increment, decrement}, dispatch) }
Note: bindActionCreators function simply combines our actions into one object.
Moving towards the component that manages the user interface.
// component/counter.js class Counters extends Component { constructor(props) { super(props); } render() { return ( {'The Redux Approach'} {this.props.count} this.props.increment()}> Increment + this.props.decrement()}> Decrement -
); } } function mapStateToProps(state) { return { count: state.count, }; } function mapDispatchToProps(dispatch) { return bindActionCreators({increment, decrement}, dispatch); } export default connect(mapStateToProps, mapDispatchToProps)(Counters);
For styling your component you can use the below code
const styles = StyleSheet.create({ mainContainer: { flex: 1, justifyContent: 'center', alignItems: 'center', }, counterNumber: { fontSize: 35, fontWeight: 'bold', }, buttonContainer: { flexDirection: 'row', }, buttonStyle: { backgroundColor: 'green', borderWidth: 1, height: 30, width: '25%', borderRadius: 5, justifyContent: 'center', alignItems: 'center', }, });
Finishing up So far we are done with
redux set up, logic, and user interface for the counter demo. Now, just a simple step remaining – import our App file in our index.js file.
// index.js import {AppRegistry} from 'react-native'; import App from './src/App.js'; import {name as appName} from './app.json'; AppRegistry.registerComponent(appName, () => App);
Git Repo link: https://github.com/sunilbacancy/CounterDemo
Context API Approach: How to Implement Context API in React Native App?
Since the context API is in-built functionality we don’t need to install thirdparty dependencies.
Folder structure Create a folder src at the root of your app. Within the src folder, we have to create 3 folders that are reducers, components, state and one file App.js.
Reducer Set Up Just like Redux, declare reducer with Context API.
// reducers/globalReducer.js export default countReducer = (state , action) => { switch (action.type) { case "Increment": return{ ...state, counter: state.counter + 1, } case "Decrement": return{ ...state, counter: state.counter - 1, } default: return{ state } } }
Create the context Create a context using createContext() and pass the initial state as arguments. You can also define without passing arguments. Define a function that will pass the data via Provider. useReducer() will take a reducer having default state, then return the updated value and dispatch the function. Inside the Provider function, use useReducer() with arguments- reducer and initial state. The returned and dispatched states are later passed as values to the Provider. // state/globalState.js
import React, {createContext, useReducer} from 'react'; import countReducer from '../reducers/globalReducer'; const initialState = { counter: 0 } export const GlobalContext = createContext(initialState); export default GlobalProvider = ({children}) => { const [state, dispatch] = useReducer(countReducer, initialState); return( {children} ) }
Providing Context After creating the context, we need to
provide the context so that it is accessible in the child components. For that, you need to wrap it within the Provider.
// src/App.js import React, { Component } from 'react'; import GlobalProvider from './state/globalState'; import Counters from './components/counter'; export default class App extends Component { render(){ return ( ) } }
Consuming Context
Use useContext() for consuming the context in respective child components.
// components/counter.js import React, { Component, useContext } from 'react'; import { View, Text, TouchableOpacity, StyleSheet } from 'react-native'; import {GlobalContext} from '../state/globalState'; const Counters = () => { const {state} = useContext(GlobalContext); const {dispatch} = useContext(GlobalContext); return(
{'The Context API Approach' } { state.counter } dispatch({type: "Increment"})} > Increment + dispatch({type: "Decrement"})} > Decrement ) } export default Counters;
For styling your component you can use the below code.
const styles = StyleSheet.create({ mainContainer: { flex: 1, justifyContent: 'center', alignItems: 'center' }, counterNumber: { fontSize: 35, fontWeight: 'bold' }, buttonContainer: { flexDirection: 'row' }, buttonStyle: { backgroundColor: 'green', borderWidth: 1, height: 30, width: '25%', borderRadius: 5, justifyContent: 'center', alignItems: 'center', }, })
Git Repo link: https://github.com/sunilbacancy/CounterContextDemo
Redux and Context API: Comparison
Redux Storing and managing values Works outside React components Avoids prop-drilling Through dispatching an action one can update the value Provides DevTools to show history of actions and state values Allows application code for triggering side effects through middlewares
Context API Not for storing or managing values Works in React components only Passes a single value be it objects, primitives, classes, etc. Avoids prop-drilling Provides current context value for Provider and Consumer but doesn’t display any history of how the value is changed. Excludes side effects mechanism – exclusively for component rendering
Conclusion
I hope the tutorial helped you understand how different Context and Redux are. Moreover, try to implement both the approaches and play around with the code to dig deeper. For more such React Native tutorials, we have a React Native Tutorials page that consists of step-by-step guidelines with the github source.
I can understand how difficult it can be to organize and manage a global store for a complex application, and also how precise you need to be for implementing Context API in your application. In case you have a large and complex project and want to implement Redux or Context API then feel free to contact us and hire React Native developer.
Thank You
www.bacancytechnology.com