Example to Use Redux in React Native App | React Native Redux

React Native Redux

Here is an example to use Redux in React Native App. We all know Redux is a state management library which will help you to manage all your state in one place called store. Integration of Redux in React Native is very easy with the help of React Redux. In this example we will see

  1. Setup of Redux library in your React Native App
  2. managing of state in a single page
  3. dispatch actions and handle them in reducers
  4. use of different state from the Redux store

We request all the readers who are new in React Native and want to learn this concept, please spend some time in understanding propsstate and components. Once you have the clear idea of that then you can start with Redux. Although creating a React Native App with Redux is very easy but without understanding the basic concepts you will find it a bit complex.

What is Redux?

Redux is a state management library which helps you to organise your application logic in one place so that your app can work as you expect. Redux makes your application’s code very easy to understand and can you can manage your logic + state regarding when, where, why, and how the state of the application is updated. It is made up of the following key parts:

  • actions
  • reducers
  • store
  • dispatch
  • selector

Actions and Action Creators

An action is a plain JavaScript object that has a type field and an optional payload. It can also be thought of as an event that describes something that has happened.

export const addToBookmark = (item) => {
  return (dispatch) => {
    dispatch({
      type: 'ADD_BOOKMARK',
      payload: item,
    });
  };
};

Action creators are just functions that create and return action objects.

Reducers

reducer is also a function that receives the current state and an action object, updates the state if necessary, and returns the new state. A reducer is not allowed to modify the existing state; instead, it copies the existing state and changes the copied values. In other words, the reducer should be a pure function. Here is an example

const initialState = {
  bookmarkItems: [],
};

const reducers = (state = initialState, action) => {
  switch (action.type) {
    case 'ADD_BOOKMARK':
      return Object.assign({}, state, {
        counter: state.counter + 1,
        bookmarkItems: [...state.bookmarkItems, action.payload],
        postData: filter_records(state.postData, [
          ...state.bookmarkItems,
          action.payload,
        ]),
        sort: 'NA',
      });
    .....
    .....
    .....
  }
};

Store

A store is an object that stores the current state of a Redux application. You create the initial state of the store by passing a reducer to the createStore function.

import {createStore} from 'redux';
import rootReducer from './src/utils/reducers';
const store = createStore(rootReducer);

Dispatch and Selectors

dispatch() is a Redux store method and is used to update the state by passing an action object.

dispatch(addToBookmark(item))

Selectors are used for reading data from a store.

Here is a flow which can help you to understand it better.

react_native_redux_flow

React Native Redux Example Description

In this example, we will create a simple bookmark App, which will have 2 screens one for the listing of bookmarks added and other to list posts. We are not going to store the bookmarks permanently in the App as this is just to understand the concept of Redux.

Whenever User lands on the Bookmark Screen he/she will see all the bookmarks added (Nothing for the first time). Whenever user wants to add bookmarks he/she can click on the add button from the bookmark screen and can see the list of post to add any post in bookmark. Once user click on any post it will disappear from the post list and will be added in the bookmark, user can see the total number of the bookmarks added in the header. When user press back button he/she will see the updated bookmark list, which can be sorted ascending/descending on the basis of post id.

For the post listing we have postData state in out store and for the bookmark we have bookmarkItems. Whenever a user lands on the post screen we will dispatch getPostsList function as an action which will be a async function for which we have used react-thunk middleware. Once we get the response of the fetch we will dispatch an action with payload which will update our postData state which will hep us to render the post list.

On click of any post we will dispatch addToBookmark action with payload which will add the clicked record in bookmarkItems and will remove that record from postData. As mentioned above user can also sort the bookmarked list which can be done using sortBookMarkList action.

I hope you understood what we are going to do so let’s start creating the React Native App with Redux.

To Make a React Native App

Getting started with React Native will help you to know more about the way you can make a React Native project. We are going to use react native command line interface to make our React Native App.

If you have previously installed a global react-native-cli package, please remove it as it may cause unexpected issues:

npm uninstall -g react-native-cli @react-native-community/cli

Run the following commands to create a new React Native project

npx react-native init ProjectName

If you want to start a new project with a specific React Native version, you can use the --version argument:

npx react-native init ProjectName --version X.XX.X

Note If the above command is failing, you may have old version of react-native or react-native-cli installed globally on your pc. Try uninstalling the cli and run the cli using npx.

This will make a project structure with an index file named App.js in your project directory.

Installation of Dependencies

To install any dependency open the terminal and jump into the project using

cd ProjectName

To use Redux in React Native we will need to install redux which is a standalone library that can be used with any UI layer or framework, We will also need to install react-redux which is the official Redux UI binding library for React.

For this example we are calling an API which is an async action and for that we will need to add redux -thunk to support the same.

npm install redux react-redux redux-thunk --save

We are using React Navigation for this example so we also need to install the dependencies for the navigation

npm install @react-navigation/native --save

Other supporting libraries for Navigation

npm install react-native-reanimated react-native-gesture-handler react-native-screens react-native-safe-area-context @react-native-community/masked-view --save

For Stack Navigator

npm install @react-navigation/stack --save

This command will copy all the dependencies into your node_module directory. –save is optional, it is just to update the dependency in your package.json file.

Project Directory/File Structure

To start with this example you need to create the following directory/file structure. Project will have a src directory with all our code, src directory will have two more directories components and utils. All these directory will have following files for complete example.

react_native_redux_exmaple_structure

You can see

  1. App.js contains main navigation
  2. src/BookmarkScreen.js, landing page which will have all the bookmarks
  3. src/PostList.js, contains the list of posts which user can add in bookmark
  4. src/components/HeaderCountLabel.js, Counter label in PostList header to show number of bookmark added
  5. src/components/HeaderSortLabel.js, to sort added bookmarks
  6. src/utils/actions.js, having all the action function definitions
  7. src/utils/reducers.js, having all the state management on the basis of actions

Code to Use Redux in React Native

App.js

// Use of Redux in React Native App | React Native Redux
// https://aboutreact.com/react-native-redux/
import 'react-native-gesture-handler';

import * as React from 'react';
import thunk from 'redux-thunk';
import {Provider} from 'react-redux';
import {createStore, applyMiddleware} from 'redux';
import {NavigationContainer} from '@react-navigation/native';
import {createStackNavigator} from '@react-navigation/stack';

import rootReducer from './src/utils/reducers';
import HeaderSortLabel from './src/components/HeaderSortLabel';
import HeaderCountLabel from './src/components/HeaderCountLabel';

const store = createStore(rootReducer, applyMiddleware(thunk));

import BookmarkScreen from './src/BookmarkScreen';
import PostList from './src/PostList';

const Stack = createStackNavigator();

const AppNavigator = () => {
  return (
    <NavigationContainer>
      <Stack.Navigator
        initialRouteName="BookmarkScreen"
        screenOptions={{
          headerStyle: {
            backgroundColor: '#f4511e',
          },
          headerTintColor: '#fff',
          headerTitleStyle: {
            fontWeight: 'bold',
          },
        }}>
        <Stack.Screen
          name="BookmarkScreen"
          component={BookmarkScreen}
          options={{
            title: 'Bookmarks',
            headerRight: () => <HeaderSortLabel />,
          }}
        />
        <Stack.Screen
          name="PostList"
          component={PostList}
          options={{
            title: 'Posts',
            headerRight: () => <HeaderCountLabel />,
          }}
        />
      </Stack.Navigator>
    </NavigationContainer>
  );
};

const App = () => {
  return (
    <Provider store={store}>
      <AppNavigator />
    </Provider>
  );
};

export default App;

src/BookmarkScreen.js

// Use of Redux in React Native App | React Native Redux
// https://aboutreact.com/react-native-redux/
import React from 'react';
import {
  FlatList,
  View,
  Text,
  SafeAreaView,
  StyleSheet,
  Image,
  TouchableOpacity,
} from 'react-native';
import {connect} from 'react-redux';
import {removeFromBookmark} from './utils/actions';

const BookmarkScreen = (props) => {
  const getItem = (item) => {
    props.removeFromBookmark(item);
  };

  return (
    <SafeAreaView style={styles.container}>
      <View style={styles.container}>
        <Text style={styles.titleStyle}>
          React Native + Redux Example
        </Text>
        <Text style={{textAlign: 'center'}}>
          Total Bookmarks: {props.counter}
        </Text>
        <FlatList
          data={props.bookmarkItems}
          ItemSeparatorComponent={
            () => <View style={styles.divider} />
          }
          renderItem={({item}) => {
            return (
              <TouchableOpacity onPress={() => getItem(item)}>
                <View style={styles.innerContainer}>
                  <Text>
                    Id: {item.id}
                    {'\n'}
                    Title: {item.title}
                    {'\n'}
                    Body : {item.body}
                  </Text>
                  <Text style={styles.itemRed}>
                    Remove from Bookmark
                  </Text>
                </View>
              </TouchableOpacity>
            );
          }}
          keyExtractor={(_item, index) => index.toString()}
        />
        <TouchableOpacity
          activeOpacity={0.7}
          onPress={() => props.navigation.navigate('PostList')}
          style={styles.touchableOpacityStyle}>
          <Image
            source={{
              uri:
                'https://raw.githubusercontent.com/AboutReact/sampleresource/master/plus_icon.png',
            }}
            style={styles.floatingButtonStyle}
          />
        </TouchableOpacity>
      </View>
      <Text
        style={{
          fontSize: 16,
          textAlign: 'center',
          color: 'grey',
        }}>
        www.aboutreact.com
      </Text>
    </SafeAreaView>
  );
};

const mapStateToProps = (state) => {
  const {counter, bookmarkItems} = state;
  return {
    counter: counter,
    bookmarkItems: bookmarkItems,
  };
};

const mapDispatchToProps = (dispatch) => ({
  removeFromBookmark: (item) => dispatch(removeFromBookmark(item)),
});

export default connect(mapStateToProps, mapDispatchToProps)(BookmarkScreen);

const styles = StyleSheet.create({
  container: {
    flex: 1,
    backgroundColor: 'white',
    padding: 10,
  },
  titleStyle: {
    fontSize: 22,
    fontWeight: 'bold',
    textAlign: 'center',
    padding: 10,
  },
  textStyle: {
    fontSize: 16,
    textAlign: 'center',
    padding: 10,
  },
  touchableOpacityStyle: {
    position: 'absolute',
    width: 50,
    height: 50,
    alignItems: 'center',
    justifyContent: 'center',
    right: 30,
    bottom: 30,
  },
  floatingButtonStyle: {
    resizeMode: 'contain',
    width: 50,
    height: 50,
  },
  innerContainer: {
    padding: 10,
  },
  itemRed: {
    color: 'red',
  },
  divider: {
    height: 0.5,
    width: '100%',
    backgroundColor: '#C8C8C8',
  },
});

src/PostList.js

// Use of Redux in React Native App | React Native Redux
// https://aboutreact.com/react-native-redux/
import React, {useState} from 'react';
import {
  View,
  Text,
  SafeAreaView,
  StyleSheet,
  FlatList,
  TouchableOpacity,
} from 'react-native';
import {connect} from 'react-redux';
import {getPostsList, addToBookmark} from './utils/actions';

const PostList = (props) => {
  useState(() => {
    props.getPostsList();
  }, []);

  return (
    <SafeAreaView style={styles.container}>
      <View style={{flex: 1}}>
        <FlatList
          data={props.postData}
          ItemSeparatorComponent={
            () => <View style={styles.divider} />
          }
          renderItem={({item}) => {
            return (
              <TouchableOpacity
                onPress={() => {
                  props.addToBookmark(item);
                }}>
                <View style={styles.innerContainer}>
                  <Text>
                    Id: {item.id}
                    {'\n'}
                    Title: {item.title}
                    {'\n'}
                    Body : {item.body}
                  </Text>
                  <Text style={styles.itemRed}>
                    Click to Add in Bookmark
                  </Text>
                </View>
              </TouchableOpacity>
            );
          }}
          keyExtractor={(_item, index) => index.toString()}
        />
      </View>
    </SafeAreaView>
  );
};

const mapStateToProps = (state) => {
  const {postData} = state;
  return {
    postData: postData,
  };
};

const mapDispatchToProps = (dispatch) => ({
  getPostsList: () => dispatch(getPostsList()),
  addToBookmark: (item) => dispatch(addToBookmark(item)),
});

export default connect(mapStateToProps, mapDispatchToProps)(PostList);

const styles = StyleSheet.create({
  container: {
    flex: 1,
    backgroundColor: 'white',
    padding: 10,
  },
  innerContainer: {
    padding: 10,
  },
  itemRed: {
    color: 'red',
  },
  divider: {
    height: 0.5,
    width: '100%',
    backgroundColor: '#C8C8C8',
  },
});

src/components/HeaderCountLabel.js

// Use of Redux in React Native App | React Native Redux
// https://aboutreact.com/react-native-redux/
import React from 'react';
import {Text} from 'react-native';
import {connect} from 'react-redux';

const HeaderCountLabel = (props) => {
  return (
    <Text style={{color: 'white', fontWeight: 'bold', padding: 10}}>
      Bookmarked: {props.counter}
    </Text>
  );
};

const mapStateToProps = (state) => {
  const {counter} = state;
  return {
    counter: counter,
  };
};

export default connect(mapStateToProps)(HeaderCountLabel);

src/components/HeaderSortLabel.js

// Use of Redux in React Native App | React Native Redux
// https://aboutreact.com/react-native-redux/
import React from 'react';
import {Text} from 'react-native';
import {connect} from 'react-redux';
import {sortBookMarkList} from '../utils/actions';

const HeaderSortLabel = (props) => {
  return (
    <Text
      style={{color: 'white', fontWeight: 'bold', padding: 10}}
      onPress={() => props.sortBookMarkList()}>
      Sort: {props.sort}
    </Text>
  );
};

const mapStateToProps = (state) => {
  const {sort} = state;
  return {
    sort: sort,
  };
};

const mapDispatchToProps = (dispatch) => ({
  sortBookMarkList: () => dispatch(sortBookMarkList()),
});

export default connect(mapStateToProps, mapDispatchToProps)(HeaderSortLabel);

src/utils/actions.js

// Use of Redux in React Native App | React Native Redux
// https://aboutreact.com/react-native-redux/
export const addToBookmark = (item) => {
  return (dispatch) => {
    dispatch({
      type: 'ADD_BOOKMARK',
      payload: item,
    });
  };
};

export const removeFromBookmark = (item) => {
  return (dispatch) => {
    dispatch({
      type: 'REMOVE_BOOKMARK',
      payload: item,
    });
  };
};

export const sortBookMarkList = () => {
  return (dispatch) => {
    dispatch({
      type: 'SORT_BOOKMARK_LIST',
    });
  };
};

export const getPostsList = () => {
  return (dispatch) => {
    fetch('https://jsonplaceholder.typicode.com/posts', {
      method: 'GET',
    })
      .then((response) => response.json())
      .then((responseJson) => {
        dispatch({
          type: 'POST_LIST_DATA',
          payload: responseJson,
        });
      })
      .catch((error) => {
        console.error(error);
      });
  };
};

src/utils/reducers.js

// Use of Redux in React Native App | React Native Redux
// https://aboutreact.com/react-native-redux/
const initialState = {
  counter: 0,
  bookmarkItems: [],
  sort: 'NA',
  postData: [],
};

const reducers = (state = initialState, action) => {
  switch (action.type) {
    case 'REMOVE_BOOKMARK':
      let newBookmarkItems =
        state.bookmarkItems.filter((bookmarkPost) => {
        return action.payload.id != bookmarkPost.id;
      });
      return Object.assign({}, state, {
        counter: state.counter - 1,
        bookmarkItems: newBookmarkItems,
      });
    case 'ADD_BOOKMARK':
      return Object.assign({}, state, {
        counter: state.counter + 1,
        bookmarkItems: [...state.bookmarkItems, action.payload],
        postData: filter_records(state.postData, [
          ...state.bookmarkItems,
          action.payload,
        ]),
        sort: 'NA',
      });
    case 'SORT_BOOKMARK_LIST':
      let sortedBookMarks = state.bookmarkItems;
      let sortOrder = 'NA';
      if (state.sort == 'NA') {
        sortedBookMarks.sort(sort_by('id', true, parseInt));
        sortOrder = 'ASC';
      } else if (state.sort == 'ASC') {
        sortedBookMarks.sort(sort_by('id', false, parseInt));
        sortOrder = 'DESC';
      } else if (state.sort == 'DESC') {
        sortedBookMarks.sort(sort_by('id', true, parseInt));
        sortOrder = 'ASC';
      }
      return Object.assign({}, state, {
        sort: sortOrder,
        bookmarkItems: [...sortedBookMarks],
      });
    case 'POST_LIST_DATA':
      return Object.assign({}, state, {
        postData: filter_records(
          action.payload,
          state.bookmarkItems
        ),
      });
    default:
      return state;
  }
};

const filter_records = (mainArray, childArray) => {
  return mainArray.filter((mainElement) => {
    if (childArray.length > 0) {
      let isReturnable = true;
      childArray.forEach((childElement) => {
        if (Number(mainElement.id) === Number(childElement.id)) {
          isReturnable = false;
        }
      });
      return isReturnable;
    } else return mainElement;
  });
};

const sort_by = (field, reverse, primer) => {
  const key = primer
    ? function (x) {
        return primer(x[field]);
      }
    : function (x) {
        return x[field];
      };
  reverse = !reverse ? 1 : -1;
  return function (a, b) {
    return
      (a = key(a)), (b = key(b)), reverse * ((a > b) - (b > a));
  };
};

export default reducers;

To Run the React Native App

Open the terminal again and jump into your project using.

cd ProjectName

1. Start Metro Bundler

First, you will need to start Metro, the JavaScript bundler that ships with React Native. To start Metro bundler run following command:

npx react-native start

Once you start Metro Bundler it will run forever on your terminal until you close it. Let Metro Bundler run in its own terminal. Open a new terminal and run the application.

2. Start React Native Application

To run the project on an Android Virtual Device or on real debugging device:

npx react-native run-android

or on the iOS Simulator by running (macOS only)

npx react-native run-ios

Output Screenshots

react_native_redux1   react_native_redux2   react_native_redux3   react_native_redux4   react_native_redux5   react_native_redux7   react_native_redux8

This is how you can use Redux in React Native App. Redux is a valuable tool, but it’s only useful when used in the right way. Some of the situations where Redux might be helpful include code that is being worked on by many people or applications that contain large amounts of application state.

If you have any doubts or you want to share something about the topic you can comment below or contact us here. There will be more posts coming soon. Stay tuned!

Hope you liked it. 🙂

6 thoughts on “Example to Use Redux in React Native App | React Native Redux”

Leave a Comment

This site uses Akismet to reduce spam. Learn how your comment data is processed.