This article is a simple step-by-step of how to start a project with React Native and Redux.
Source Code (Branch init)

Create-react-native-app

Following the path of its excellent web brother, I recommend the use of the create-react-native-app to bootstrap your new project. This one configures all the build tools necessary to create a React Native app, and gives you some excellent Expo integrations.
First thing to do is to install CLI tool with
npm install -g create-react-native-app
Now, run the following command with the name you want to give to your project
react-native init my_awesome_app
Go forward to the new folder, and run
react-native run-android

Redux

This article is not a lesson about Redux and all its merits. Our goal with this tutorial is to create a simple app that fetches some data from the web, store it in a redux store, and present this information on the screen in a list format. For this example, we are going to use the GitHub API to fetch all the repositories owned by a specific user.
First, let's install some dependencies:
npm install axios redux-axios-middleware redux react-redux --save
  • axios: our HTTP client
  • redux-axios-middleware: as the name implies, it is a redux middleware that intercepts all actions that are dispatched with a payload.request (example in the following section)
  • redux and react-redux: standard redux dependencies for React

Reducer

Next, we are going to create our action types, reducer, and actions. I have been using the ducks-modular-redux pattern in my projects, and it has been an excellent way to organize my code. This pattern enforces the definition of all the redux components in a single file, for example, named reducer.js.

export const GET_REPOS = 'my_awesome_app/repos/LOAD';
export const GET_REPOS_SUCCESS = 'my_awesome_app/repos/LOAD_SUCCESS';
export const GET_REPOS_FAIL = 'my_awesome_app/repos/LOAD_FAIL';

export default function reducer(state = { repos: [] }, action) {
  switch (action.type) {
    case GET_REPOS:
      return { ...state, loading: true };
    case GET_REPOS_SUCCESS:
      return { ...state, loading: false, repos: action.payload.data };
    case GET_REPOS_FAIL:
      return { ...state, loading: false, error: action.payload.error };
    default:
      return state;
  }
}

export function listRepos(user) {
  return {
    type: GET_REPOS,
    payload: {
      request: {
        url: `/users/${user}/repos`
      }
    }
  };
}


Let's deep dive into all the details. When we call the listRepos function, the action GET_REPOS is dispatched with a payload.request content. The redux-axios-middleware intercepts this action and eventually make an HTTP request to the GitHub API. After that, it will, automatically, dispatch either a GET_REPOS_SUCCESS or a GET_REPOS_FAIL action, depending on the status of the request.
If you look at the reducer function, you see that we are returning a new state based on all the dispatched actions, being the most important the GET_REPOS_SUCCCESS where we extract the list of repositories from the action.payload (response from the API).


middleware from redux in action

List Component

With the reducer ready, we can create our list component. Create a file calledRepoList.js and paste the following content:

import React, { Component } from 'react';
import { View, Text, FlatList, StyleSheet } from 'react-native';
import { connect } from 'react-redux';

import { listRepos } from './reducer';

class RepoList extends Component {
  componentDidMount() {
    this.props.listRepos('relferreira');
  }
  renderItem = ({ item }) => (
    <View style={styles.container}>
      <View style={styles.item}>
        <Text>{item.name}</Text>
      </View>
      <View style={styles.item}>
        <Text>{item.id}</Text>
      </View>
    </View>
  );
  render() {
    const { repos } = this.props;
    return (
      <FlatList
        styles={styles.container}
        data={repos}
        renderItem={this.renderItem}
      />
    );
  }
}

const styles = StyleSheet.create({
  container: {
    flex: 1,
    flexDirection: 'row',
    alignItems: 'flex-start',
    justifyContent: 'center'
  },
  item: {
    padding: 16,
    width: '50%',
    borderBottomWidth: 1,
    borderBottomColor: '#ccc'
  }
});

const mapStateToProps = state => {
  let storedRepositories = state.repos.map(repo => ({ key: repo.id, ...repo }));
  return {
    repos: storedRepositories
  };
};

const mapDispatchToProps = {
  listRepos
};

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

From this file, it is essential to highlight the use of the FlatList component to render our list of repositories. This choice is crucial to guarantee a good performance on mobile devices with limited processing power. Another thing to notice is the use of the connect function that glue our component to the Redux store. This currying function accepts two parameters:
  • mapStateToProps: map the state of the redux store to the component props. In our case, we are getting the repos from the state, adding a key attribute to each object, necessary for the FlatList, and, finally, returning a new object that is passed to the props.
  • mapDistpatchToProps: map the dispatched actions to the component props. In our example, the goal is to dispatch the action returned by the listRepos function.
On the componentDidMount lifecycle component event, we, finally, dispatch the listRepos action to request the repositories from the API.

Redux Initialization

Finally, all you need to do now is change de App.js file with the following piece of code:

import React, { Component } from 'react';
import { StyleSheet, Text, View } from 'react-native';
import { createStore, applyMiddleware } from 'redux';
import { Provider, connect } from 'react-redux';
import axios from 'axios';
import axiosMiddleware from 'redux-axios-middleware';

import reducer from './reducer';
import RepoList from './RepoList';

const client = axios.create({
  baseURL: 'https://api.github.com',
  responseType: 'json'
});

const store = createStore(reducer, applyMiddleware(axiosMiddleware(client)));

export default class App extends Component {
  render() {
    return (
      <Provider store={store}>
        <View style={styles.container}>
          <RepoList />
        </View>
      </Provider>
    );
  }
}

const styles = StyleSheet.create({
  container: {
    flex: 1,
    backgroundColor: '#fff',
    marginTop: 50
  }
});

The changes that glue all the parts are the creation of the redux store with the use of the axios middleware:
const store = createStore(reducer, applyMiddleware(axiosMiddleware(client)));
Moreover, the use of the Provider component as a wrapper to our application and responsible for providing access to the created store.

Result

If everything goes well, when issue another npm start, you should see a screen like this one:




Author: Renan Ferreira