React Native with Redux - For complete beginners
Well, I don't think I need to talk much about React Native here, do I?. What is Redux then? let's hear from the official redux repository. Well, Redux is a predictable state container for JavaScript applications. It helps us to write applications that behave consistently, run in different environments and are easy to test. We can use Redux with React, React Native, or any other view library, it's a tiny 2KB library, but never judge a thing by it's size. 😎
Integrating redux for the first time in to your application can be a little messy. But believe me, once you get past the initial difficulties. I'm sure you will find it a lot easier than you thought it would be. Before getting our hands dirty with the code, let's just go through a few terms that we are gonna use a lot when working with Redux.
Actions: Actions are payloads of information that send data from your application to your store. Store can be updated with the help of actions, means, any state change necessary, the change required will be dispatched via actions from the react component. Every action in redux will have a type and based on the action type, the 'reducer' will evaluate the action and will determine the new state.
Reducers: Actions describes that something has happened within the application and the application state has to be updated, but how?. This is where the reducer kicks in. When an action is dispatched for state change, it's the reducer's duty to make the necessary changes to the state and return the new state of the application. The reducer does so by evaluating the type of the action dispatched by the react component.
Store: An immutable JavaScript object that stores the state of your entire application. You need only one store for your entire application. The store should only be updated via actions and reducers.
Middleware: A function that accepts store as parameter and return dispatch function that accepts action as it's parameter. This returned dispatch function will then override the existing store dispatch function. We will be using 'redux-thunk' in this example as the middleware.
Components: This is where we keep all our screens/user interfaces. The components or containers will be plain JavaScript files returning our view.
Without further explanations, let's jump right into our project coding. In this tutorial, we will build a simple single screen application which will fetch some data from the internet and then display it on our application home screen. Since this is a single screen application, we won't be using a navigation libraries such as React navigation and also, we will be keeping a simple application folder structure to accommodate our component, action, reducer and the action types. This type of folder structure is fine if your application doesn't have to deal with a total number of screens no more than ten. If the number is higher than that, I personally recommend you to choose a better folder structure by dividing your application into different modules.
Let the war begin💻
Create React Native Project
We will be using the React Native cli to build our project. If you need help installing the react native cli, please follow this link.
react-native init ReactReduxExample
Sit back and relax while it creates the project for you. Once done, now it's time to install the required dependencies. Let's do that.
cd ReactReduxExample
npm install redux --save npm install react-redux --save npm install redux-thunk --save or npm install redux react-redux redux-thunk --save
Create a Project Structure
As I was saying, this is a pretty simple application with only a single screen. So, to keep things simple, we will choose a simple folder structure. Inside your project root folder, we will create an 'app/src' folder. This is where we will keep all our application related code. Inside the app folder, we will create an 'actions' folder, a 'reducers' folder, an 'actionTypes' folder, a 'store' folder and a 'components' folder. The actions folder will manage the actions and each action types are specified inside the actionTypes folder. The reducer folder will cover all the application reducers, which in our case is just one. We will create our store inside our store folder and the components folder will contain the entire user interface of our application.
Project folder Structure |
Create your first action*
The action is a basic function called from the component whenever we want the whole state of the application to be changed. It is a simple function returning the action itself and the type of the action.
app/src/actions/home_actions.js
import {
FETCHING_DATA,
FETCHING_DATA_SUCCESS,
FETCHING_DATA_FAILURE
} from '../actionTypes/home_actionTypes';
export function fetchHomeFromAPI() {
return (dispatch) => {
fetch('https://randomuser.me/api/?results=10&nat=gb')
.then(res => res.json())
.then(json => dispatch(getHomeSuccess(json)))
.catch(err => dispatch(getHomeFailure(err)))
}
}
function getHomeSuccess(data) {
return {
type: FETCHING_DATA_SUCCESS,
data
}
}
function getHomeFailure() {
return {
type: FETCHING_DATA_FAILURE
}
}
The fetchHomeFromAPI() is used to fetch data from the internet. After the API call, required actions are dispatched with the data obtained from API.
Create your first reducer*
Reducers are the one in charge of updating the state of the application. Redux will pass the current state of the application and the action occurred. It's up to the reducer to update the state of the application based on the action type.
app/src/reducers/home_reducer.js
import {
FETCHING_DATA,
FETCHING_DATA_SUCCESS,
FETCHING_DATA_FAILURE
} from '../actionTypes/home_actionTypes'
const initialState = {
home: [],
isFetching: true,
error: false
}
const homeReducer = (state = initialState, action) => {
switch (action.type) {
case FETCHING_DATA_SUCCESS:
state = Object.assign({}, state,
{ home: action.data,
isFetching: false,
error: false
});
return state;
case FETCHING_DATA_FAILURE:
return {
...state,
isFetching: false,
error: true
}
default:
return state
}
}
export default homeReducer;
app/src/reducers/index.js
import {combineReducers} from 'redux'
import homeReducer from './home_reducer'
const rootReducer = combineReducers({
home: homeReducer
})
export default rootReducer
Create actionTypes*
ActionTypes are basically certain constants that are used to define the type of the action being dispatched. The application state is updated with respect to the type of action dispatched.
app/src/actionTypes/home_actionTypes.js
export const FETCHING_DATA = 'FETCHING_DATA'
export const FETCHING_DATA_SUCCESS = 'FETCHING_DATA_SUCCESS'
export const FETCHING_DATA_FAILURE = 'FETCHING_DATA_FAILURE'
Create your store*
app/src/store/configureStore.js
import { createStore, applyMiddleware } from 'redux'
import reducers from '../reducers'
import thunk from 'redux-thunk'
const configureStore = () => {
const middlewares = [thunk];
const enhancer = applyMiddleware(...middlewares);
return createStore(reducers, enhancer)
}
export default configureStore;
Create your user interface*
app/src/components/home_component.js
import React, { Component } from 'react';
import {
StyleSheet,
ScrollView,
RefreshControl,
FlatList,
View,
Text,
Dimensions,
Image
} from 'react-native';
import { connect } from 'react-redux';
import { fetchHomeFromAPI } from '../actions/home_actions'
const { width, height } = Dimensions.get('window');
class Home extends Component {
constructor(props) {
super(props);
this.state = {
refreshing: false,
}
}
componentDidMount() {
this.props.getHome()
}
_onRefresh() {
this.setState({ refreshing: true })
this.props.getHome()
this.setState({ refreshing: false })
}
render() {
return (
<View style={styles.container}>
<ScrollView
showsVerticalScrollIndicator={false}
refreshControl={
<RefreshControl
refreshing={this.state.refreshing}
onRefresh={this._onRefresh.bind(this)}
/>
}
>
<FlatList showsVerticalScrollIndicator={false}
data={this.props.home.results}
renderItem={({ item }) =>
<View style={styles.user}>
<Image source={{ uri: item.picture.medium }} style={styles.image} />
<View>
<Text style={styles.name}>
{item.name.first} <Text>{item.name.last}</Text>
</Text>
<Text style={styles.email}>{item.email}</Text>
</View>
</View>
}
ItemSeparatorComponent={() =>
<View style={styles.ItemSeparator}>
</View>
}
keyExtractor={(item, index) => index.toString()}
/>
</ScrollView>
</View >
)
}
}
const styles = StyleSheet.create({
container: {
flex: 1,
backgroundColor: '#FFFFFF',
},
user: {
flexDirection: 'row',
marginLeft: 15,
alignItems: 'center'
},
userProfile: {
justifyContent: 'center'
},
image: {
width: width * 0.16,
height: width * 0.16,
borderRadius: width * 0.16 / 2,
},
name: {
fontSize: width / 23,
color: '#000000',
marginLeft: 15,
},
email: {
fontSize: width / 26,
color: '#3d3e3e',
marginLeft: 15,
},
ItemSeparator: {
width: width - 30,
borderWidth: 0.2,
borderColor: '#3d3e3e',
margin: 15
}
})
function mapStateToProps(state) {
return {
home: state.home.home
}
}
function mapDispatchToProps(dispatch) {
return {
getHome: () => dispatch(fetchHomeFromAPI())
}
}
export default connect(mapStateToProps, mapDispatchToProps)(Home)
With the help of 'connect' imported from 'react-redux', we will connect and map everything to our user interface
mapStateToProps()
It contains all the state properties that is available to the components of the application.
mapDispatchToProps()
It contains all the necessary functions wrapped with action creators which will be then mapped to the props of the app and can be accessed directly.
Let's wrap it up*
Now we have everything ready, It's time to wrap all of our app into one single place. Redux needs to inject a store holding the app into the application. To do so, it requires a 'Provider' wrapping the entire application. we will do that by adding these code into our main 'index.js' file.
index.js
/** @format */
import React from 'react';
import { AppRegistry } from 'react-native';
import { name as appName } from './app.json';
import { Provider } from 'react-redux';
import configureStore from './app/src/store/configureStore';
import Home from './app/src/components/home_component';
const store = configureStore()
const ReactReduxExample = () => (
<Provider store={store}>
<Home />
</Provider>
)
AppRegistry.registerComponent(appName, () => ReactReduxExample);
That's it! Yeahhh😎
Testing time*
It's almost time to see the results, excited huh? Without wasting any more time, let's take a look at what we were doing.😁
On Android
react-native run-android
On iOS
react-native run-ios
Our application |
Well done.
If you enjoyed coding with me, feel free to checkout this Github repo as well and stay tuned for more.
To get more updates and hacking news,
Follow me on Twitter
Follow me on Twitter
Comments
Post a Comment