Managing State in React Native With Redux Toolkit
Noble Okafor
Posted on July 20, 2022
When developing robust web or mobile applications(apps), handling state internally, as is the norm with React Native components, isn't always ideal. If done improperly, it can quickly become very messy. A library tool like Redux is advised in these circumstances.
This tutorial will teach you how to use Redux Toolkit to manage the state flow in a React Native app.
What is Redux?
Redux is a Javascript global state management library designed to act as a central store for managing application state. Redux helps you build apps that behave consistently across all environments by tracking all changes made to the app state.
What is Redux Toolkit?
Redux Toolkit is Redux's official toolset for developing efficient React-Redux apps. It was designed to simplify the writing of common Redux logic and resolve the usual difficulties of using the core Redux library.
such as:
- Setting up the Redux store
- Creating reducer state slices
- Writing immutable state updating code
Redux Glossary
Action
An Action is an object that indicates a desire to modify a state in the Redux store. Actions must include a payload and a type attribute that specifies the state change's nature. Reducers are required to carry out Actions.
Reducer
A Reducer is a pure function that takes two arguments; the current state and an action to return a new state result. Reducers do not modify the original state directly; rather, they make a copy of the state and modify that.
Dispatch
A "dispatch function" is a function that accepts either a synchronous or asynchronous action object and sends it to a reducer for execution.
Slice
A collection of reducers and actions that work together to implement a single app feature.
Store
According to the official Redux documentation, a store is an object that holds the app's entire state tree. Redux can only have a single store in an app.
Using Redux Toolkit with React Native
Prerequisites
- Node.js LTS >= v14.x.x (v16.14.2 recommended).
- A package manager like npm or yarn installed.
- Expo CLI installed.
- Knowledge of Redux concepts.
NOTE: This article is not a tutorial on React Native and will not be focusing on React Native concepts.
Project Setup
The app you will build will be a random color generator, and to keep things simple, you will be using Expo CLI to create and run the project.
Run the following commands in your local machines terminal:
expo init redux-toolkit-guide
cd redux-toolkit-guide
For the template, select ‘— Managed Workflow—blank’.
Install the app's necessary dependencies, including @react-navigation/native
, react-native-screens
, and react-native-safe-area-context
.
See the official React Navigation library documentation for the latest installation instructions.
yarn add @react-navigation/native
expo install react-native-screens react-native-safe-area-context
You can find the app's complete source code in this GitHub repo.
Building the App Interface
Open the redux-toolkit-guide app in a code editor and create the following file structure.
- Create a src folder in the project's root.
- Inside src create a store folder.
- Inside store, create a colorSlice.js and store.js file.
- Create a screens folder inside src.
- Inside screens, create a HomeScreen.js file and import the code below.
//HomeScreen.js
import React from "react";
import { StatusBar } from "expo-status-bar";
import {
Text,
View,
StyleSheet,
TouchableOpacity,
FlatList,
} from "react-native";
const HomeScreen = () => {
return (
<View>
<StatusBar style="dark" />
<TouchableOpacity
onPress={() => //empty anonymous function}
style={styles.button}
>
<Text style={{ fontSize: 20 }}>Generate Random Color</Text>
</TouchableOpacity>
<FlatList
keyExtractor={(item) => item}
data={color}
style={{ marginTop: 15 }}
renderItem={({ item }) => {
return (
<View
style={{
backgroundColor: item,
height: 150,
width: 150,
alignSelf: "center",
margin: 10,
}}
/>
);
}}
/>
</View>
);
};
export default HomeScreen;
const styles = StyleSheet.create({
button: {
alignSelf: "center",
borderWidth: 1,
borderRadius: 10,
padding: 10,
marginTop: 20,
},
});
HomeScreen.js
Override App.js with the following code:
//App.js
import * as React from "react";
import { NavigationContainer } from "@react-navigation/native";
import { createNativeStackNavigator } from "@react-navigation/native-stack";
import HomeScreen from "./src/screens/HomeScreen/HomeScreen";
const Stack = createNativeStackNavigator();
function App() {
return (
<NavigationContainer>
<Stack.Navigator>
<Stack.Screen name="Home" component={HomeScreen} />
</Stack.Navigator>
</NavigationContainer>
);
}
export default () => {
return <App />;
};
App.js
Run expo start
in your terminal to start the app:
Setting Up the Redux store
Install the Redux toolkit and React-Redux packages in your project:
npm install @reduxjs/toolkit react-redux
Redux toolkit creates a store using the configureStore API in place of the createStore from core Redux.configureStore
automatically sets up the Redux DevTools Extension and some middleware.
Inside store.js create a redux store:
import { configureStore } from "@reduxjs/toolkit";
export const store = configureStore({
reducer: { },
});
colorSlice.js
The store holds a single root reducer object for all the state slices in the app.
Now wrap the app component in App.js with the store
using the React-Redux Provider
. This ensures the Redux store
is at the topmost level and is available to the entire React Native app:
//App.js
import { store } from "./store/store";
import { Provider } from "react-redux";
export default () => {
return (
<Provider store={store}>
<App />
</Provider>
);
};
App.js
Creating Redux Toolkit Slices
Next, you will create a state slice to handle all action and reducer functions relevant to generating a random color in the app. Importing and calling createSlice
, define inside it;
- a
name
to identify the slice. - an
initialState
value. This specifies the state on the first app render (similar to theuseState
hook). - a
reducer
function to determine how the state is to be updated. With the spread operator, update thestate.value
with the result of therandomRgb()
.
When writing core Redux logic, it is essential to avoid directly mutating the state value. But, with createSlice
available through Redux Toolkit, you can write mutating code in reducers and have it converted into immutable copies:
//colorslice.js
import { createSlice } from "@reduxjs/toolkit";
const initialState = {
colors generated
value: [],
};
//A function to generate random RGB values
const randomRgb = () => {
const red = Math.floor(Math.random() * 256);
const green = Math.floor(Math.random() * 256);
const blue = Math.floor(Math.random() * 256);
return `rgb(${red}, ${green}, ${blue})`;
};
//State slice
export const colorSlice = createSlice({
name: "color",
initialState,
reducers: {
setColor: (state) => {
state.value = [...state.value, randomRgb()];
},
},
});
// Action creators are automatically generated for each case reducer function
export const { setColor } = colorSlice.actions;
export default colorSlice.reducer;
colorSlice.js
NOTE: Only write mutating code using the createSlice
or createReducer
APIs.
Redux toolkit allows you to create action objects automatically. Above, you have set the case functions defined in our reducer to colorSlice.actions
. The name of the reducer will be used as the action type when creating an action creator.
Afterward, import and add the created slice to the root reducer of the store
:
//store.js
import colorSlice from "./colorSlice";
export const store = configureStore({
reducer: {
color: colorSlice,
},
});
store.js
useSelector and useDispatch
You have successfully set up a Redux state management system. Now, you need to be able to read the current state inside HomeScreen.js and dispatch an action to the reducer.
Use the useSelector hook to access the redux state and the useDispatch hook to dispatch actions:
//HomeScreen.js
import { useDispatch, useSelector } from "react-redux";
import { setColor } from "../../../store/colorSlice";
const HomeScreen = () => {
const color = useSelector((state) => state.color.value); //reading the state
const dispatch = useDispatch();
return (
<TouchableOpacity
onPress={() => dispatch(setColor())} //Dispatch action
>
<Text style={{ fontSize: 20 }}>Generate Random Color</Text>
</TouchableOpacity>
<FlatList
data={color}
/>
);
};
export default HomeScreen;
HomeScreen.js
Above, you have imported useDispatch and useSelector from React-Redux. You have also imported the created setColor reducer. Reading the current state with state.color.value
, you set its value as the data entry for the Flatlist. Then, by calling useDispatch()
as dispatch and passing setColor()
as its argument, you have sent the action to the appropriate reducer case.
That's a wrap! Your React Native app can now generate random colors.
When to Use Redux?
The app you built is ideally too basic to use a state manager like Redux. However, this tutorial aims to introduce Redux Toolkit in the most basic way possible.
So when should you use Redux?
- When there's a considerable amount of data changing over time
- When you need to track state changes
- When dealing with deeply nested components, and passing state becomes problematic
- When multiple components require access to the same piece of state
Conclusion
This tutorial covered what Redux and Redux Toolkit are and the basic terminologies. It also covered the steps for integrating Redux Toolkit into a React Native application;
- Creating a store with configureStore
- Providing that store to the app
- Creating reducer slices with createSlice
- and reading and updating the state with useSelector and useDispatch
I recommend visiting the official Redux documentation for further research.
Posted on July 20, 2022
Join Our Newsletter. No Spam, Only the good stuff.
Sign up to receive the latest update from our blog.