Click here to share this article on LinkedIn »
In this tutorial, we will learn how to add Redux to a React Native application and how to integrate it with Firebase.
By the end of this tutorial, you should be able to pull data from Firebase into Redux, update that data in real-time, and display that data in your screens and components.
Setup
To use this tutorial, you should have a React Native application that is up-and-running and has Firebase initialized.
Installing Libraries
We are going to use 3 Redux libraries, which you can install by typing the following in a terminal in your project directory:
npm install -S redux react-redux redux-thunk
Create Some Sample Data
Head over to the Firebase Console and add some sample data to work with.
Basic Redux Infrastructure
Disclaimer: This is how I personally setup Redux for simple applications. Other tutorials usually say to split up the different pieces into separate files. I like to have everything in one file for convenience.
Create Redux File:
Create a new folder called redux
and inside of that create a file called app-redux.js
.
Import Libraries:
Inside of app-redux.js
, import a few Redux libraries that we will need soon. And throw Firebase in there too, because we’ll need that eventually. :)
import { createStore, applyMiddleware } from 'redux';
import thunkMiddleware from 'redux-thunk';
import * as firebase from 'firebase';
Setup Initial State:
Below that, create a little section of code for your initial state:
//
// Initial State...
//const initialState = {
personData: { },
}
This data structure represents the initial state of your application. Usually you will want this to be blank or filled with default values. In our case, we are going to load the data from Firebase, so we just want an empty object to begin with.
Setup Reducer:
Create another section of code for your reducer:
//
// Reducer...
//const reducer = (state = initialState, action) => {
return state;
}
The reducer will take an action
and return a new state
based on what the action tells it to do. For now, since we don’t have any actions, we will just return the state as-is.
Setup Store:
Create another section of code for your store:
//
// Store...
//const store = createStore(reducer, applyMiddleware(thunkMiddleware));
export { store };
The store
is a data store that will keep track of all your data. Notice that we passed in the reducer
that we created earlier. We are also applying the thunkMiddleware
, which will be useful a little later. Finally, we export
the store
so we can use it in other files.
Connect Your App to Redux
Now that we have the basic Redux framework setup, we need to connect it to our app. Open up your App.js
and import the following. Notice that we’re importing the store
that we just created.
import { Provider } from 'react-redux'
import { store } from './redux/app-redux';
Surround your app’s content with the Provider
component and supply it with our store
. Here’s how mine looks:
This will now give your app access to the Redux store that we created.
Connect Your Screen to Redux
Now we need to connect our individual screens up to Redux. You don’t have to do this for all of your screens — only the ones that you want to have access to Redux data.
Import Libraries:
First, import connect
from react-redux
:
import { connect } from 'react-redux';
Create Stub Functions:
Then, create these two functions (we will fill them in later):
const mapStateToProps = (state) => {
return { };
}const mapDispatchToProps = (dispatch) => {
return { };
}
Change Export:
My screens were all set up to be exported like this:
export default class HomeScreen extends React.Component {
So we need to remove the export default
to get this (we will export it somewhere else):
class HomeScreen extends React.Component {
Export with Connect:
Finally, at the very bottom of your file, export your class like this:
export default connect(mapStateToProps, mapDispatchToProps)(HomeScreen);
We’re calling connect
and passing in those two functions we created. Then we pass our screen to the result of that call.
Final Screen:
For reference, here is what my screen looks like:
Now your screen is connected to Redux, but we will come back later to retrieve and display the data.
Actions
Now, we will create some actions that we can use in Redux to update our data.
Standard Action:
Back in app-redux.js
, create another section of code for Action Creators
. Action creators
get sent to your reducer
and contain information about how to update the state
. Here we will create an action creator
that can update our state.personData
object. We will also export
it so it is accessible in other files.
Note: You can put any data you want in the return object. But it is a Redux convention to always have a type
property.
//
// Action Creators
//const setPersonData = (personData) => {
return {
type: "setPersonData",
value: personData
};
};export { setPersonData };
Since action creators
get sent to the reducer
, we need to update the reducer to be able to handle this action. Scroll up to your reducer
and update it like so:
const reducer = (state = initialState, action) => {
switch(action.type) {
case "setPersonData":
return { ...state, personData: action.value }; default:
return state;
}
}
See how we are switch-ing
on action.type
? That’s why we should always include a type
property in our action creators
.
Notice that when the type
is "setPersonData"
, we return a new state
where personData
is changed to the action.value
. This will update our state and anywhere in the app that is accessing that data.
Thunk:
Ordinarily, action creators
can only return objects. But since we applied the thunkMiddleware
, we can also have action creators
that return functions. This is crucial for actions that don’t have data immediately available (such as asynchronous network calls), or for data that updates in real-time (such as Firebase realtime updates).
Let’s create another action that will watch our Firebase data in real-time.
const watchPersonData = () => {
return function(dispatch) {
firebase.database().ref("person").on("value", function(snapshot)
{
var personData = snapshot.val();
var actionSetPersonData = setPersonData(personData);
dispatch(actionSetPersonData); }, function(error) { console.log(error); });
}
};
Here, we are returning a function
that is given a dispatch
as a parameter. Inside the function, we call Firebase’s realtime function .on("value")
for the "person"
node in our database. Any time that function is successful (which will be anytime our data is updated), we will grab the data from snapshot.val()
, get a setPersonData
action creator, and then dispatch
that action to our reducer
(which will then update the state
). If the function fails, we just log the error in the console.
Sound like a lot? It is, but see how it all ties together in the next section.
PS: Don’t forget to export
your new action creator!
export { setPersonData, watchPersonData };
View Data in Screen
Back in our HomeScreen.js
, we need to update those functions that we initially created, mapStateToProps
and mapDispatchToProps
. These functions will actually add props
to your screen/component so you can access them to render the data.
Before updating them, let’s import
the watchPersonData
action creator that we just created.
import { watchPersonData } from './../redux/app-redux';
mapStateToProps:
Now update mapStateToProps
to give your screen access to state.personData
:
const mapStateToProps = (state) => {
return {
personData: state.personData
};
}
This takes the state
(from Redux) and creates a property in your screen called personData
whose value comes from state.personData
. Now, anywhere in your screen, you can display personData
. Try it out!
<Text>{this.props.personData.firstName}</Text>
<Text>{this.props.personData.lastName}</Text>
mapDispatchToProps:
The only problem now is that we never started watching for data!
Update your mapDispatchToProps
function to add a property to your screen called watchPersonData
.
const mapDispatchToProps = (dispatch) => {
return {
watchPersonData: () => dispatch(watchPersonData())
};
}
This now adds a function property to your screen called watchPersonData
that, when called, will dispatch
the watchPersonData
action (which then calls the Firebase API for watching data in our "person"
node.
Now, in your screen’s constructor, call this.props.watchPersonData()
.
Test:
Test it out by running your application. You should now see the personData
displayed on your screen. Go into Firebase and change the data and come back to your application. You should see the updated data!