React Native Redux: The Complete Guide

32
2061
React Native Redux Tutorial Demo

Redux is a standalone state management library that can be used with any library or framework. For example, if your background is React developer, you have used the Redux library with React.

React Native Redux

To persist data in React Native application, use the redux library. The primary use of Redux is that we can use one application state as a global state, and interacting with the state from any react component is very easy, whether they are siblings or parent-child.

It would be best if you connected React Native app to the redux store to persist data in your application.

Enough theories. Let’s head to the practical, and we start our project by installing React Native CLI globally. You can skip the following step if you have already installed it.

Step 1: Install React Native.

Type the following command.

npm install -g react-native-cli

Okay, now, type the following command to create a new application.

react-native init rncreate
cd rncreate

After installing, we need to open this application in the two different Simulators.

For testing on the iOS simulator, type the following command.

react-native run-ios

If you have configured XCode correctly, then one iOS device will pop up, and the development server will start.

To open the project inside the Android Simulator, type the following command.

react-native run-android

Install the redux and react-redux libraries using the following command.

yarn add redux react-redux

# or

npm install redux react-redux --save

Step 2: Add Textbox and Button into the App.js.

We will add a text box and button to add places. So let us add the TextInput and Button. Also, we will add the flexbox layout. Write the following code inside the App.js file.

// App.js

import React, {Component} from 'react';
import { StyleSheet, View, TextInput, Button } from 'react-native';

export default class App extends Component {

placeSubmitHandler = () => {
    console.log("Submitted");	
}

render() {
   return (
    <View style={ styles.container }>
       <View style = { styles.inputContainer }>
        <TextInput
           placeholder = "Seach Places"
           style = { styles.placeInput }
        ></TextInput>
        <Button title = 'Add' 
            style = { styles.placeButton }
            onPress = { this.placeSubmitHandler }
        />
        </View>
    </View>
    );
  }
}

const styles = StyleSheet.create({
  container: {
    paddingTop: 30,
    justifyContent: 'flex-start',
    alignItems: 'center',
  },
  inputContainer: {
    flexDirection: 'row',
    justifyContent: 'space-between',
    alignItems: 'center',
    width: '100%'
  },
  placeInput: {
    width: '70%'
  },
  placeButton: {
    width: '30%'
  },
  listContainer: {
    width: '100%'
  }
});

Step 3: Define the state and input handler.

We need a state to manage. So we define the initial state like the following.

// App.js

state = {
   placeName: '',
   places: []
}

placeSubmitHandler = () => {
    console.log("Submitted");	
}

Step 4: Create the following folders inside Root.

Create the following folders.

  1. actions
  2. reducers
  3. components

Inside the actions folder, create one file called types.js. Then, add the following line inside types.js.

export const ADD_PLACE = 'ADD_PLACE'

The action type is the reducer’s operation type. The reducer case will be executed based on the action type, and we will modify the state to remain pure. So we create a copy of the existing state and return a new state.

Now, create one more file called place.js in the same directory.

// place.js

import { ADD_PLACE } from './types';

export const addPlace = placeName => {
  return {
    type: ADD_PLACE,
    payload: placeName
  }
}

The addPlace() function returns an action. Based on that action, the reducers function’s case is executed.

But we need to connect this action to our App.js component somehow. Otherwise, we can not add the data to the Redux store.

Also, we need first to create a store. But before, we also need to create a reducer function. So, create a reducer, create a store, and then connect the React Native application to the Redux store.

Step 5: Create a Reducer function.

Inside the reducers function, create one file called placeReducer.js. Add the following code inside it.

// placeReducer.js

import { ADD_PLACE } from '../actions/types';

const initialState = {
  placeName: '',
  places: []
};

const placeReducer = (state = initialState, action) => {
  switch(action.type) {
    case ADD_PLACE:
      return {
        ...state,
        places: state.places.concat({
          key: Math.random(),
          value: action.payload
        })
      };
    default:
      return state;
  }
}

export default placeReducer;

So, here, we have defined the function called placeReducer, which accepts the two arguments.

  1. state
  2. action

The first time, it will take the initial state of our application, and then we pass whatever argument; it takes that argument and operates based on the case execution.

The second argument is action, which consists of type and payload. The payload is the place name; we entered the text box. So it adds the text box’s value inside the places array.

Remember here; we have returned to a new state, not an existing one. So we have purely modified the state and not an existing state.

Step 6: Create a Redux Store.

Create a file called store.js inside the root folder and add the following code.

// store.js

import { createStore, combineReducers } from 'redux';
import placeReducer from './reducers/placeReducer';

const rootReducer = combineReducers({
  places: placeReducer
});

const configureStore = () => {
  return createStore(rootReducer);
}

export default configureStore;

Here, we have created the redux store and passed the reducer to that store. Finally, the combineReducer function combines all the different reducers and forms the global state. So this is the worldwide state of our whole application.

Step 7: Connect the redux store to the React Native app

To connect the redux store to react native application, use the <Provider> component. Inside the root folder, you will find one file called index.js, and inside that file, add this code.

// index.js

import { AppRegistry } from 'react-native';
import React from 'react';
import App from './App';
import { name as appName } from './app.json';
import { Provider } from 'react-redux';

import configureStore from './store';

const store = configureStore()

const RNRedux = () => (
  <Provider store = { store }>
    <App />
  </Provider>
)

AppRegistry.registerComponent(appName, () => RNRedux);

It is almost the same as React web application, in which we pass the Provider as a root element and pass the store, and then via react-redux’s connect() function, we can connect any React component to the redux store.

Step 8: Connect React Native app to the Redux store.

Finally, we connect our App.js component to the Redux store. Again, we need the connect() function from the react-redux library.

// App.js

import React, { Component } from 'react';
import { StyleSheet, View, TextInput, Button, FlatList } from 'react-native';
import ListItem from './components/ListItem';
import { connect } from 'react-redux';
import { addPlace } from './actions/place';

class App extends Component {

  state = {
    placeName: '',
    places: []
  }

  placeSubmitHandler = () => {
    if(this.state.placeName.trim() === '') {
      return;
    }
    this.props.add(this.state.placeName);
}

placeNameChangeHandler = (value) => {
  this.setState({
    placeName: value
  });    
}

placesOutput = () => {
   return (
    <FlatList style = { styles.listContainer }
      data = { this.props.places }
      keyExtractor={(item, index) => index.toString()}
      renderItem = { info => (
        <ListItem 
          placeName={ info.item.value }
        />
      )}
    />
  )
}

render() {
  return (
    <View style={ styles.container }>
      <View style = { styles.inputContainer }>
        <TextInput
          placeholder = "Seach Places"
          style = { styles.placeInput }
          value = { this.state.placeName }
          onChangeText = { this.placeNameChangeHandler }
        ></TextInput>
        <Button title = 'Add' 
          style = { styles.placeButton }
          onPress = { this.placeSubmitHandler }
        />
        </View>
        <View style = { styles.listContainer }>
          { this.placesOutput() }
        </View>
      </View>
    );
  }
}

const styles = StyleSheet.create({
  container: {
    paddingTop: 30,
    justifyContent: 'flex-start',
    alignItems: 'center',
  },
  inputContainer: {
    flexDirection: 'row',
    justifyContent: 'space-between',
    alignItems: 'center',
    width: '100%'
  },
  placeInput: {
    width: '70%'
  },
  placeButton: {
    width: '30%'
  },
  listContainer: {
    width: '100%'
  }
});

const mapStateToProps = state => {
  return {
    places: state.places.places
  }
}

const mapDispatchToProps = dispatch => {
  return {
    add: (name) => {
      dispatch(addPlace(name))
    }
  }
}

export default connect(mapStateToProps, mapDispatchToProps)(App)

When we click the add button, we get the textbox value and send it to the action with that value.

So now that action returns the object with the action type and payload, and based on the type, the reducer will be executed and add that value inside the store.

If the store’s values are changed, we need to update the UI based on the new values. That is why the mapStateToProps function is created.

So, when the store’s places array gets the new value, the render function executes again and updates the UI.

The mapDispatchToProps function helps us connect our application to the necessary action so that action is then further executed by a reducer and changes the application state.

Also, inside the components folder, create one file called ListItem.js and add the following code inside it.

// ListItem.js

import React from 'react';
import { View, Text, StyleSheet, TouchableOpacity } from 'react-native';

const ListItem = (props) => {
    return (
      <TouchableOpacity>
        <View style = { styles.listItem }>
          <Text>{ props.placeName }</Text>
        </View>
      </TouchableOpacity>
    );
}

const styles = StyleSheet.create({
  listItem: {
    width: '100%',
    padding: 10,
    marginBottom: 10,
    backgroundColor: '#eee'
  }
});

export default ListItem;

This component receives the properties from the parent component and displays the data correctly. Save the file and go to your Simulators, and refresh the screen.

React Native Redux Example Tutorial From Scratch

That’s it for React Native Redux Example. Thanks for taking it.

32 Comments

      • Hi there,

        Could you mind help me to check this error “Could not find “store” in the context of “Connect(App)”. Either wrap the root component in a , or pass a custom React context provider to context consumer to Connect(App) in connect option

        • It should be written as connect(…)(App)

          Where, the “…” contains the functions “mapStateToProps” and “mapDispatchToProps”

          mapStateToProps as the name suggests, maps the the store’s variables to the component’s props.
          mapDispatchToProps maps the actionable methods (actions) to the component’s props.

          Take a look at Section #8 (Connect React Native app to Redux store) of the article to better understand the concept.

          Hope this helps!

  1. Hello. I’ve followed your tutorial. But I got following error.
    Invariant Violation: Could not find “store” in either the context or props of “Connect(App)”. Either wrap the root component in a , or explicitly pass “store” as a prop to “Connect(App)”.

  2. I’ve followed your tutorial. But I get error. How can I solve this?
    Invariant Violation: Could not find “store” in either the context or props of “Connect(App)”. Either wrap the root component in a , or explicitly pass “store” as a prop to “Connect(App)”.

  3. Thank for the tutorial but I have an issue regarding index.js for:
    AppRegistry.registerComponent(appName, () => RNRedux);

    I don’t have that file on my setup 🙁

  4. I have this error: Could not find “store” in either the context or props of “Connect(App)”. Either wrap the root component in a , or explicitly pass “store” as a prop to “Connect(App)”.
    Me and 2 people above have problem with this, i don’t think his code work

  5. Hey Krunal, thanks for the tutorial. The code is running fine, but can you make video of this code for better explanation. Why each and every line is coded. That will a great help.

  6. In App Component, you are using local state using this.state instead one should use state.props.places to use common state. In the same way different handlers in App Component are pointing to local store with this.state.
    Please update the implementation of App Component to complete the redux integration.

  7. Having a reducer with state in it… something seems very off about this setup. I’d caution people to learning redux via this article to possible look elsewhere.

      • I got it…
        in app.js in step #8 towards the bottom line:-

        const mapDispatchToProps = dispatch => {
        return {
        add: (name) => {
        dispatch(addPlace(name))
        }
        }
        }

        this.props.add(this.state.placeName) refers to the method add created in mapDispatchToProps used to dispatch the method addPlace with params this.state.placeName.

Leave A Reply

Please enter your comment!
Please enter your name here

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