Integrating React Native with Redux
How we use React Native with Redux @Magnetis
One of the fundamental questions when writing real time apps in the 21st century is how to model the managed state of the application. The state is the layer that will dictate what data structures needs to be saved, what values are required, what actions derive from button clicks, what needs to be updated and how often that happens. These can be simple when dealing with simple relationships and small applications, but it can get very ugly quite soon as the application grows in size and complexity.
When building React Native applications, it is crucial that the state may be handled in a very effective and secure way. Especially, because you are writing plain JavaScript, a language that you can do pretty much anything you want, that can throw errors of undefined
objects anywhere and that does not give you the safety of statically typed languages. That is some of the reasons why the JavaScript, React and React Native community in general advise you to use some sort of state management framework to model your apps that deal with complex state, Redux is one of them.
Now, lets say you are building a Bitcoin wallet and you want to be able to model the click on a button that fetches the Bitcoin price in dollar and displays it to the user. In order to start we need to get a few things out of the way first.
- Install React Native + Depedencies
Install all the dependencies React Native requires by following the guides here and stop at the command to create a new app. (Make sure to select the Buiding Projects with Native Code
option when following the tutorial).
2. Download/Clone the Project Starter
Download or clone this repository on the branch project-starter
.
3. Install dependencies locally
$ npm install
4. Build the app locally running
# For iOS users
$ react-native run-ios# For android users
$ react-native run-android
The app should look something like this:
State
The problem at hand can be abstractly modeled to 2 things, when the user hits the button, it should go into the state of loading
, waiting for the response to arrive, once the response arrives we should set the text displayed to the value. For us to accomplish that, we need:
- Save the object that the response of the request gives us
- Save the fact that the request is loading
- Save any other errors that might occur
Price request/response
The first problem we have is that we want to make a networking request that will fetch the latest Bitcoin price in USD from a web service. So we will need to have a type that contains a request and response.
Types
Types are merely an abstraction of the type
parameter that the actions receive.
- On
src/state/price/types.js
write the following types there.
Actions
Now we going to create the actions associated with this type. In order to do that, we are going to install another dependency that helps us create those actions, by introducing an API (createAction
)to reduce the amount of boilerplate code we have to write. The first argument it receives is the type of that action, and the second one is the function that will receive the payload this action receives. In our case, you can see that during the request action (when the user hits the Get Price
button), we don’t need any data with it, so we are canceling the function’s need (this is needed to avoid redux-actions
passing a huge unnecessary payload for every action you create, makes your app really slow during debugging). However, when we receive the response and communicate back to redux that the data has arrived, actions are used to transport the payload that response is carrying.
npm install redux-actions — save
- On
src/state/price/actions.js
write the following snippet:
Commands
Now that we got our types and actions modeled, we need to think what commands will those actions execute, this is where we add the code that has the possibility of generating side effects. In order to deal with those hairy bits of our code we are going to use redux-loop’s concept of commands to execute those tasks, commands are used when you want to describe a piece of code that has some sort of intention that affects the state outside the store’s scope. In our case, our command will be responsible for making a request to the server to fetch the price.
As you can see below, we created a request function and wrapped it inside redux-loop’s function Cmd.run
, which also is responsible for passing a sucessActionCreator
indicating the action that should be dispatched once the request is successful, you can also pass the failActionCreator
if you want to handle the failure case as well.
npm install redux-loop --save
- Create the file below on
src/state/price/cmds.js
.
Reducers
This is where all of the set up we have been doing is going to be executed, once some action is dispatched, the consequence it has on the state of the application is managed by the reducers. Our reducer exports the handleActions
function that receives as the first argument an object whose keys are the action types
we previously defined, and values are the functions that will be called once the action happens, and the second parameter is the default state, which in this case is:
price: null
error: null
loading: false
We can see that when an action is reduced to whatever is you want it to do. We handle both PRICE.REQUEST
and PRICE.RESPONSE
differently, this is because the consequences those actions have on the system are different:
PRICE.REQUEST
: In this action we want to execute a command that will reach out to the server. So we need to use redux-loop’sloop
function, this function receives the state you want to pass once the action happens, and the second parameter indicates the command you want to be executed. In our case we want thepriceRequest
.PRICE.RESPONSE
: In this action we want to check if there was an error thrown in consequence of the request or if the request was successful. All we have to do in this case, is return a new state with the changes.- On
src/state/price/reducer.js
write the following snippet:
Selectors
Once the reducer is out of the way, we need to create a way where our view will be able to consume the data we just gathered through our requests, the way we do that is through selectors. In this example, all we want is the USD value of Bitcoin that we received from our networking request. To achieve this, we are going to install another dependency called reselect
, this library is just a wrapper that creates the selectors for you, removing some boilerplate code you would have to write yourself.
npm install reselect --save
- On
src/state/price/selectors.js
write:
In order to expose those selectors for use in the react native part of our application, we are going to create another selectors file, that will be responsible for exposing any other selectors we create within our app.
- On
src/state/selectors.js
write:
Creating the store
Now that we have managed to create the business logic of our redux modeling, we can go on to plug it by creating the reducer that is responsible for combining everything and creating the store. When creating the store, we have to install()
redux-loop middleware and combineReducers(...)
that are going to be reduced by our store. For that we need to introduce the redux
npm package.
npm install redux --save
- On
src/state/reducer.js
write:
Connecting the Store
Once we created the store we need a way to safely communicate where this store will be consumed, we do that by wrapping our app in a react-redux Provider
and passing our store as a property to this component, think of a provider as a parent component to your entire application.
npm install react-redux --save
- On
src/index.js
write:
Connecting Redux to Components
Now that we have our redux setup created, we can create a container component that will work as the interface between our state and our view component. To accomplish that, we re going to connect the selectors to the data we want to display or share between state and view, and the actions we want to communicate back to the state when something happens at the component level.
- On
src/view/container.js
write:
Now that we have our container created, its time to connect it to the view, the src/view/index.js
. In this file we simply declare an App
component that receives usdPrice, priceRequest and loading
as props
and pass that app as the component to be used in the container.
Once you save and run the app and hit the button, you should be able to get a price, like this:
At last,
There is a certain learning curve to adopting redux for your applications, and there will be times where you are trying to pull your hair out because you can’t understand what is going on with your code. On the flip side, once you get over that steep little learning mountain out of the way, there will be a very powerful tool that will help you build safer and better applications. In addition to that, you gain a power of being able to extend this simple modeling that we did to a more robust and complex scenario, maintaining the same semantics, safety and with little additional overhead. I have only touched at the surface of what is possible, but I hope I have gotten you curious enough to go explore some more Redux + React Native on your own.
Feel free to ask me any questions on twitter and/or in the comment sections, also you can check the full version of the app here. In addition to that, you can also find the snippets for all the codes here.