React Native — Handling State

Arno van Rossum
FourScouts
Published in
5 min readMar 30, 2018

Your boss or you have a great idea for a mobile application, enthusiastic as you are, you check the Internet and figured out you *must* use React Native. It works on both android and iOS with the same code base, how convenient!

You create your first React Native app, it sets up the environment for you and creates a default application.
When inspecting the code, it is just a simple view with no dynamic content whatsoever. No problem to add some Views, Texts, and Scrollview’s to make this app look great.

Alright, it needs to retrieve some data, so lets introduce call to an API. “fetch” is the library to use and seems easy enough.

But then it hits you…. How am I going to show the results of the API within my app? What is the proper way to handle state?

In comes state and props.

Each react component has 2 types of data that live inside the component.

props

“props” is state that will not change in the lifetime of your component.
In simple words this means that the props supplied to the react component are read only.
They could only be changed by a parent which will destroy the component and create a new one. They are accessed via this.props.something. And created with:

<YourComponent something=”value” />

state

“state” is state that might change during the lifetime of your component.
When your component is loaded, you are able to change values of the state within the component itself. Doing so will change the view of the component accordingly.

In this example internal state is used to pick up news from an API.

import React, {Component} from 'react';
import {ScrollView, View, Text} from 'react-native';
export class News extends Component {
constructor(props) {
super(props);
this.setState({
news: []
});
}
componentWillMount() {
fetch('http://mywebsite.com/news.json')
.then((response) => response.json())
.then((responseJson) => {
this.setState({
news: responseJson.news
});
})
.done();
}
render() {
return (
<ScrollView>
{this.state.news.map(
(newsItem) => {
return (<View><Text>{newsItem.content}</Text></View>);
}
);}
</ScrollView>
);
}
}

This is a fine way to handle dynamic state and helps a lot with fast prototyping your app. Especially when your app mainly consists of views.

Problems start to arise when you have multiple views and you start mutating data. Your application starts growing in complexity when different components start depending on the same state but will only update individually on state change.
You probably start to fix it by creating some sort of event listener model, and might even bind specific components to each other.
The problem with these kind of solutions is that the flow of data gets more obscure.
Which results in harder debugging of your app.
Eventually your application code starts to degrade and applying quick patches gets attractive.

And when your easy fix looks like this you are definitely not proud of yourself.

if ( this.state.something ) {
if ( this.state.something == 2 && this.state.somethingelse ) {
// Render something
} else {
// Render something else
}
} else {
// Render something otherwise
}

In comes Flux

**The** alternative for using component state is [Flux](https://facebook.github.io/flux/)
it’s about actions, a dispatcher, stores, views and seems like the proper way to do it.

when googling what flux implementation to use for react-native you are most likely to be hit with [Redux](http://redux.js.org/). Which is not a full flux implementation, but is commonly used.

Redux

According to the website, “Redux is a predictable state container for JavaScript apps.” Should fit the solution, extracting state from the react component and making it more understandable.
You read the readme, the concept seems easy. There is only one store which keeps your state, and is updated via reducers.

The first thing you need to do is adding a store, the store holds the state of your mobile app.

Redux gives you a component to create a store for this:

import { createStore } from 'redux';const store = createStore();

Now, lets add the store to my React Native App.

import { Provider } from 'react-redux';const App = () => (
<Provider store={store}>
<App />
</Provider>
);
AppRegistry.registerComponent('MyApp', () => App);

Oke, the redux store is available, but how do I do an Async API call?
Redux has a great topic about [Async Actions](http://redux.js.org/docs/advanced/AsyncActions.html) it requires some middleware like thunk to make Async Actions available.

import thunkMiddleware from 'redux-thunk';const store = createStore(
applyMiddleware(
thunkMiddleware
)
);

Done!

Back to the original goal, calling an API. Lets make actions for this. Actions are payloads of information that send specific data from your application to your store.

function receiveNews(news) {
return {
type: 'RECEIVED_NEWS',
news: news,
receivedAt: Date.now()
};
}
export const fetchNews = () => {
return function (dispatch) {
return fetch('http://mywebsite.com/news.json')
.then((response) => response.json())
.then((json) => {
dispatch(receiveNews(json))
}
).catch((error) => {
console.error(error);
});
};
};

When the action is called, the reducer should pick up the payload and return desired state for the store.

So the reducer:

const fetchNewsReducer = (state = [], action) => {
switch (action.type) {
case 'RECEIVED_NEWS':
return action.news;
break;
default:
return state
}
};
export default fetchNewsReducer;

Lets add the reducer to the store, this way each action is picked up by the reducer

import { fetchNewsReducer } from './app/reducers/fetchNewsReducer';const store = createStore(
fetchNewsReducer,
applyMiddleware(
thunkMiddleware
)
);

The store is updated with news information when actions are dispatched, the last step is to make sure your view is connected to your store. When the store is updated, the view will automatically be updated with its new properties.

import { connect } from 'react-redux';
import { News } from 'views/news';
const mapStateToProps = state => {
return {
news: state.news
}
};
const mapDispatchToProps = dispatch => {
return {
fetchNews => {
dispatch(fetchNews())
}
};
}
export const ContainerNews = connect(
mapStateToProps,
mapDispatchToProps
)(News);

The method mapStateToProps, receives state from the redux store, and maps this information to properties available within the News view. The method mapDispatchToProps gives you the ability to make actions available as property for your view.

Your news view would end up like this:

import React, {Component} from 'react';
import {ScrollView, View, Text} from 'react-native';
export class News extends Component {
constructor(props) {
super(props);
this.setState({
news: []
});
}
componentWillMount() {
this.props.fetchNews();
}
render() {
return (
<ScrollView>
{this.props.news.map(
(newsItem) => {
return (<View><Text>{newsItem.content}</Text></View>);
}
);}
</ScrollView>
);
}
}

Great redux works! But that was painfully cumbersome.

For something trivial like retrieving news, you need to add a reducer, connect the reducer to your store, an action, and finally a container component to connect redux to your properties.
It seems like a good idea to have separation of concerns, but when you just want to retrieve some info, it results in an overly complex mobile app.

Sometimes, you just need to get some data. Placing it within the react component could make it easier to read and easier to follow and fits the purpose.

--

--