This article is a simple step-by-step of how to start a project with React Native and Redux.
Source Code (Branchinit
)
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`
}
}
};
}
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 called
RepoList.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);
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 theFlatList
, 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 thelistRepos
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
}
});
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
Thanks for sharing this nice and informative blog with us.
ReplyDeleteFull Stack Training in Hyderabad
Full Stack Training in Ameerpet
Thanks for sharing this nice and informative blog with us
ReplyDeletejava online training in hyderabad
Nice blog..! I really loved reading through this article. Thanks for sharing.
ReplyDeleteReact JS training in hyderabad
You have written an informative post. Looking forward to read more.
ReplyDeleteProfessional Web Development Services
Excellent Blog! I would Thanks for sharing this wonderful content.its very useful to us.I gained many unknown information, the way you have clearly explained is really fantastic.
ReplyDeleteoracle training in chennai
oracle training institute in chennai
oracle training in bangalore
oracle training in hyderabad
oracle training
hadoop training in chennai
hadoop training in bangalore
This comment has been removed by the author.
ReplyDelete