React, Redux and Rainy Day Funds: A Step-By-Step Tutorial

If you found this article, you’re very likely two things:

  1. Trying to improve your computer programming skills/understanding.
  2. Insecure about your personal finances — aren’t we all?

Well, in this tutorial, you’ll learn how to deploy an app using React, Redux and React Router. Check out the live version of the app here.

As a bonus, you’ll learn the basics of a core, oft-overlooked aspect of personal finance — the emergency fund, a.k.a. rainy day fund.

Why listen to what I have to say?
I work in financial advisory services full-time. I study and implement personal financial concepts more waking hours than not. So naturally, when learning to program, the finance creeps into the code (and vice versa).
This is my first tutorial, and first implementation of React and Redux — but hey, gotta start somewhere!

Getting Started

Install create-react-app.

npm install -g create-react-app

In the terminal, cd to the folder where you want to store your project and create a new React app. We’ll be calling the app rainy-day-fund. Once it installs, change to the newly created directory.

create-react-app rainy-day-fund
cd rainy-day-fund

Start the app.

npm start

If that command didn’t do so automatically, open http://localhost:3000/ in your browser and you should see the landing page for create-react-app. It’s as simple as that to set up our environment. Thanks, Facebook Incubator! If you have a minute, take a look in your new project’s node_modules folder — imagine setting all that up on your own!

Initial Prep

Open up your rainy-day-fund directory in your text editor of choice and you’ll see the initial project structure that comes with create-react-app.

rainy-day-fund/
node_modules/
public/
favicon.ico
index.html
src/
App.css
App.js
App.test.js
index.css
index.js
logo.svg
.gitignore
package.json
README.md

Let’s make some initial changes before getting to the good stuff:

  • Delete App.css, App.test.js, and logo.svg. I housed all of the app’s CSS in index.css to keep things simple and easy to copy, and we won’t be doing any testing — no need to complicate things when learning the basics.
  • Copy the contents of index.css from the GitHub repo into your file.
  • In index.html, change the title to “Rainy Day Fund App”, and add the following link for our font (or any font you like):
<link href="https://fonts.googleapis.com/css?family=Titillium+Web" rel="stylesheet">
  • Create a components folder in src and add App.js to it. Note: We’ll be in src for most of this tutorial, so I’ll omit the src and other prefixes when referencing unique file names.
  • In App.js, replace the contents within the return statement with some simple placeholder text and remove the App.css import. Your App.js should now look like this:
//src/components/App.js
import React from 'react';
const App = () => {
return (
<p> Hello World! </p>
);
}
export default App
  • In index.js , update the file path for the App component. Reload your browser and you should see “Hello World!”

You’ll likely want this project on GitHub, especially if you want to deploy to GitHub pages at the end, so be sure to initialize a repo for this project and add it to GitHub.

React-Bootstrap

React-Bootstrap is a simple, efficient way to style our app and take advantage of a library of useful components.

To install React-Bootstrap , run the following commands:

npm install react-bootstrap --save
npm install bootstrap@3 --save

Next, import the Bootstrap CSS file into src/index.js. Make sure you do this ABOVE the import for index.css , like this:

import 'bootstrap/dist/css/bootstrap.css';
import 'bootstrap/dist/css/bootstrap-theme.css';
import './index.css';

Now, whenever we want to utilize Bootstrap components, we make named imports at the tops of our files, like this:

import {FormControl, InputGroup } from 'react-bootstrap';

Our First Component — The Landing Page

As a way to ease into this— and make clear that this little practice app in no way should be used alone to make financial decisions, just to illustrate the concept of a rainy day fund — let’s make a simple landing page component.

Add a new file called Landing.js in your components folder. Let’s make it a presentational component.

You can read fantastic articles here and here about presentational components and their counterparts, container components. If you’re not yet familiar with them, I HIGHLY recommend that you explore those links and others before continuing. The concepts I find most helpful:

Presentational Components:

  • are written as stateless functional components, sometimes called functional components, simple, dumb, etc. Here’s another great resource.
  • take the props object, or individually/explicitly named props, as an argument
  • only have a render function / return block (when kept simple — all of the presentational components in this tutorial will be simple)

Container Components:

  • pass state, handlers, and other types of information/instructions down to presentational components
  • have nothing to do with how the app looks
  • help connect React and Redux — more later!

Now, import and render the Landing component in App.js.

//src/components/App.js
import React, { Component } from 'react';
import Landing from './Landing'
const App = () =>  {
return (
        <Landing/>
    );
}
export default App;

If you tried to load the app as is, you probably got an error about the module ‘react-router’ not being found. Well, that’s because we haven’t installed it yet! Cue React-Router.

React Router

React-Router makes it amazingly easy to render components based on URL routes. Let’s get to it.

First, install React-Router.

npm install --save react-router

Next, create a file in src called router.js and make it look like this:

//src/router.js
import React from 'react'
import { Router, Route, hashHistory } from 'react-router'
import App from './components/App'
export default(
<Router history={hashHistory}>
<Route path="/" component={App} />
</Router>
)

Here we’re exporting a component (nameless for now) to handle the routing for our app. It starts off with a Router component that contains our individual routes, which are created using Route components. Each individual Route takes a URL path and and a component to render at that path.

Take note of our use of hashHistory. From the react-router docs:

Hash history works without configuring your server, so if you’re just getting started, go ahead and use it. In general, though, production web applications should use browserHistory for the cleaner URLs, and for support for server-side rendering, which is impossible with hashHistory.

So just know it’s a simple choice for letting us eventually deploy on GitHub pages and move on to the rest of our app.

Lastly, because the Router is rendering App and should be the top-level component of our app (for now…), we need to import it into index.js and render it in the root of index.html.

At this point, the app should be rendering the Landing component. If you want to be absolutely sure that App and Landing are rendering through router, check out the React Developer Tools (and install them if you haven’t done so already).

Not the Personal Finance Lesson You Wanted — But the One You May End Up Needing Someday

Alrighty, create a new component file called WhatIsARainyDayFund.js. This is a pretty terrible name for a component, but you can probably guess what it’s purpose will be. Check it out below — familiarizing yourself with these basic concepts will help you understand the app we’re about to build.

We’re going to make WhatIsARainyDayFund the first step of our app after reaching the landing page. This’ll require a bit of reshuffling:

First, open router.js where we’re going to make these updates:

  • import the WhatIsARainyDayFund and Landing components
  • reconfigure the App route from being self-closing to having open and closed Route tags. This will allow us to nest child routes within the main App route.
  • import IndexRoute from react-router and nest an instance of IndexRoute in the App route. Because we want to see the landing page when we first visit the app, let’s make Landing the component for the IndexRoute.
  • Nested in the App route, add a Route for our WhatIsARainyDayFund component

The final product should look like this:

//src/router.js
import React from 'react'
import { Router, Route, hashHistory, IndexRoute } from 'react-router'
import App from './components/App'
import Landing from './components/Landing'
import WhatIsARainyDayFund from './components/WhatIsARainyDayFund'
export default (
<Router history={hashHistory}>
<Route path="/" component={App}>
<IndexRoute component={Landing} />
<Route path="what-is-a-rainy-day-fund" component= {WhatIsARainyDayFund} />
</Route>
</Router>
)

To render our app’s nested route-to-component pairings, we need to go to App.js and pass the this.props.childrenobject, which corresponds to the nested routes rendering at any particular route in our Appcomponent. And because App is written as a functional component, we explicitly pass the children object as an argument. We’ll also add some Bootstrap elements.

Check out our new App.js below.

//src/components/App.js
import React from 'react';
import { Grid, Row } from 'react-bootstrap';
const App = ({children}) => {
return (
<Grid id="App">
<Row>
{children}
</Row>
</Grid>
);
}
export default App;

If all went according to plan, when you open http://localhost:3000 and click the button to enter the app, you’ll see the rendered WhatIsARainyDayFund component, where you can read a nicer-looking overview of the concept.

The Rest of Our Presentational Components

Next we’re going to build out the rest of our presentational components so that we have the general structure and look of our app. I’ll explain the significance of each component as we go so you can begin to think about the information/data we’ll need to manage. We’re going to make plenty of changes to some of these components in a bit when user input and Redux come into play, so keep in mind that some of the code snippets are not the finished versions!

MenuBar.js

We’ll also need to add MenuBar to App.js so that it renders throughout the app. So update App.js like this:

Assumptions.js

Before you start thinking about how much to save for a rainy day fund, there are some basic assumptions to consider, as they will help dictate how much to save and how quickly you can reach your savings goal. For this simple little app, we only have three assumptions:

  1. Relationship status — whoa, we’re already into some pretty personal stuff! But the purpose of this is to gauge how many sources of income you or your household benefit from. If you only have one source of income, whether single or just the sole earner of a couple, you’re more susceptible to financial hardship if you lose your job, have some big expense, etc., and therefore should probably have a bigger financial cushion. On the other hand, if you’re in a relationship where both of you work, or you’re a badass with multiple, solid income streams, then you’ll likely have at least one source of income should the other dry up.
  2. Monthly, after tax income — this is the amount of money that goes into your bank account every month. It’s what you have to spend and, for our app’s purpose, save after taxes, 401k contributions, and all of that fun stuff.
  3. Initial Savings — how much cash do you have tucked away “just in case?” This is that number. These aren’t savings already devoted to that new car, engagement ring or trip someplace tropical — it’s what you’d put to use on your hypothetical “rainy day.”

Expenses.js

If you lose your job, you’ll still have bills to pay and things to buy. These expenses don’t go away when you’d like them to, so your rainy day fund needs to cover these expenses long enough to figure things out. This is definitely the most time-consuming, and most revealing, part of the app. The user is forced to consider how much she spends and whether she could continue to spend that much if things go south.

We’ll also create a reusableExpense component that represents a single expense, each with a name and dollar amount, as well as a TotalExpenses component.

Expense.js

TotalExpenses.js

SavingsPlan.js

Here’s where we will bring it all together and put the user’s supplied data (for now just dummy data) to work in crafting a super simple approach to establishing a rainy day fund.

Based on the user’s expenses, income, initial savings and income sources, the app will create a savings goal to get to a fully-funded rainy day fund. Knowing the savings goal, the user can experiment with how much she saves every month, and how many months it’ll take to reach her goal.


To render all of these components, we need to update router.js.

Let’s also take a minute to include some helper functions in helpers.js:

If you haven’t already, be sure to grab the app’s CSS ==from GitHub!

Redux — Getting Started

Redux is “a predictable state container for JavaScript apps.” We could make this app using React state alone, but where’s the fun in that?

Redux is decently confusing when starting out — for me, this is my first (and at the time of this writing only) usage of it. Before I started incorporating Redux, I dove deeply into the Redux docs, especially the basics, the sections on usage with React and React-Router, as well as the To-Do List example. I suggest — nay, I plead! — that you do the same. Also check out this, this and this. Also Dan Abramov, author/co-author of Redux, create-react-app and other tools, has a video series on Redux. Make some coffee, get cozy and have at it!

When you think you’re ready, install Redux.

npm install --save redux

We’re going to install two additional packages — React Redux and the Redux developer tools — that we’ll need.

npm install --save react-redux
npm install --save-dev redux-devtools

State — The Big Picture

Redux state is “the single source of truth” — all of it is stored in a single object. Before we dive in, think about the various pieces of information from previous sections that we’ll want to manage. How should we structure the state object of our app?

I decided to structure the state to mirror the structure of the app, just to keep things easy to follow. It will eventually look like this:

{
assumptions: {
incomeSources
monthlyIncome
initialSavings
},
expenses: [expenseObj1, expenseObj2, ...],
savingsPlan: {
monthlySavings
}
}

We’re going to follow one piece of information — monthlyIncome, i.e. the user’s monthly income — from setting things up all the way to seeing the user’s inputted income in our store via the Redux dev tools.

First Action & Action Creator

Per the Redux docs:

Actions are payloads of information that send data from your application to your store. They are the only source of information for the store.

In src, create a new file called actions.js.

First step is to declare (and export for use in our reducers) a type property of the action object, which will take the form of a string. Let’s give it a name that makes it clear that we want to update our store with the user’s inputted income.

export const UPDATE_INCOME = 'UPDATE_INCOME';

The action object will have this type property as well as the information we want to send to our store. An example below:

{
type: UPDATE_INCOME,
income: 3000
}

We need an action creator to “create” our action, i.e. return the action object in a function. This function takes as an argument(s) the data we want to update our state with. Our action creator for updating the user’s income looks like this:

const updateIncome = (income) => {
return {type: UPDATE_INCOME, income}
}

Note the use of ES6 shorthand for initializing properties from variables when adding the income argument to our action object.

actions.js should now look like this:

//src/actions.js
export const UPDATE_INCOME = 'UPDATE_INCOME';
export const updateIncome = (income) => {
return {type: UPDATE_INCOME, income}
}

First Reducer

Now that we have our action, we need a reducer to dictate how our app’s state changes based on the type and payload of the action it processes. Check out the docs on handling actions.

Create a new folder called reducers within src and add a new file to it called reducers.js. Here’s the code, which I’ll explain in a sec:

//src/reducers/reducers.js
import { UPDATE_INCOME } from '../actions'
const initialAssumptionsState = {
income: 0,
}
const assumptionsReducer = function(
state=initialAssumptionsState,
action
){
switch(action.type){
case UPDATE_INCOME:
return Object.assign({}, state, {income: action.income})
     default: return state;
}
}
export {assumptionsReducer};

From top to bottom:

  • import the UPDATE_INCOME action type from actions.js
  • create the initial state for the reducer. This is NOT the “single source of truth” state mentioned before, but the piece of state that this reducer will update. Eventually, initialAssumptionsState will include default values for our two other assumptions.
  • create the reducer assumptionsReducer, passing state (with an ES6 default argument) and an action.
  • tell assumptionsReducer to expect the UPDATE_INCOME action.type as a case in the switch statement.
  • return a new state (remember, just a slice of our one true state) by filling an empty object with the previous state, updated with the income provided by our action.

Be sure to read up on Object.assign() if you’re unfamiliar, because it allows us to adhere to one of Redux’ three principles — that reducers must be pure functions.

We only have this one reducer right now, but we’ll eventually have multiple reducers to handle different actions and update different slices of our app’s state. So let’s create an index.js file for the reducers folder and use Redux’ combineReducers() helper function.

//src/reducers/index.js
import { combineReducers } from 'redux';
import { assumptionsReducer } from './reducers';
const appReducer = combineReducers({
assumptions: assumptionsReducer,
});
export default appReducer;

Creating the Store

Now we’re ready to set up a Redux store that will manage and maintain our app’s state. We’ll do this in src/index.js, using the React Redux binding tool we installed previously.

We made a few updates:

  • imported createStore(), Provider, and our appReducer
  • created our store and hooked it up to the browser’s dev tools
  • rendered our router through React Redux’ Provider component that will allow us to connect the store to the rest of the app.

Open up your Redux dev tool, take a look in the “state” tab and you should see the assumptions piece of our state that we initialized in reducers.js. Now it’s time to allow the user to update our state’s income from the front-end.

Connecting React and Redux

We’re going to use React Redux’ connect() function to create container components that subscribe to our store and facilitate communication between our presentational components — the ones the user will interact with — and the store.

In the components folder, create a new component file called AssumptionsContainer.js.

//src/components/AssumptionsContainer
import { connect } from 'react-redux';
import { updateIncome } from '../actions'
import Assumptions from './Assumptions'
const mapStateToProps = (state) => {
return {
assumptions: state.assumptions,
}
}
const mapDispatchToProps = (dispatch) => {
return {
handleUpdateIncome: (income) => {
dispatch(updateIncome(
Number((income).replace(',',''))
))
}
}
}
const AssumptionsContainer = connect(
mapStateToProps,
mapDispatchToProps
)(Assumptions)
export default AssumptionsContainer

Let’s quickly walk through what’s going on, top to bottom:

  • import what we need — connect() to facilitate the making of our container component, theupdateIncome action creator to use when new data needs to be dispatched to our store and change the state, and the Assumptions component that will communicate user intervention up to the container.
  • mapStateToProps — we choose what slice(s) of Redux state to pass as props for use in the Assumptions component. Here we only need the assumptions slice of our overall state that has the income property (remember, from reducers.js).
  • mapDispatchToProps — we define a function to use an an event handler in the Assumptions component. It takes the user’s inputted income and passes it to the updateIncome action creator, which returns an action with the desired type UPDATE_INCOME and payload income. That action is dispatched to our store, returning a new, updated state.

For this all to work, we need to make a few more changes.


In router.js, replace the Assumptions component (at the import and in the Route) with the AssumptionsContainer component.

Next, update Assumptions to pass the props we defined in AssumptionsContainer and handle an update to income when the user changes the corresponding input field (onBlur()) .

Open your Redux dev tool, change the income input field, click somewhere outside the input field and see the income property’s value change!

The Rest of Redux

What follows are the components and updated files to get our app fully setup with Redux. I’m going to blaze through them. As you add more actions, reducers, containers, etc., play around with the Redux dev tool. If you don’t understand something, you have the Github repo, Google, documentation, Stack Overflow, and the comments to figure things out — if it’s a bit challenging to follow, then you know you’re learning. And if you’re totally lost, try out some of the linked resources in earlier sections.

NOTE: If you want to check that each container component and updates to presentational components have successfully implemented as you go, then you’ll need to go into router.js and replace the original component with the newly created, corresponding container component, like we did for AssumptionsContainer.

actions.js

reducers/reducers.js

reducers/index.js

AssumptionsContainer.js

Assumptions.js

At this point, you should be able to see the full Reduxstate tree in the Redux developer tools and update the assumptions using the corresponding input fields.

ExpensesContainer.js

Expenses.js

Expense.js

TotalExpenses.js

At this point, if you’ve correctly updated router.js to render the new ExpensesContainer component, you should be able to add and delete expenses, update their names and amounts (which you can see happen in real time with your dev tools) and watch the total expenses amount update with updates to individual expenses.

SavingsPlanContainer.js

SavingsPlan.js

router.js

Adding Local Storage

If everything went according to plan, and your app functions like the one here, then you’re ready to add local storage. Once it’s added, users will be able to refresh their browser without losing their data. If you want more detail, check out this video from Dan Abramov.

Create a file in src called localStorage.js.

Update src/index.js.

Deploy to GitHub Pages

Rather than let our app languish on our computers, we’re going to deploy it to GitHub pages for the world to see. Thankfully, create-react-app makes this incredibly easy. The instructions that follow are basically verbatim from the create-react-app user guide.

Go into package.json and add a homepage field, which will be your personal GitHub page at YOUR_USERNAME.github.io domain followed by rainy-day-fund .

//rainy-day-fund/package.json
{
name: "rainy-day-fund",
homepage: "https://YOUR_USERNAME.github.io/rainy-day-fund",
version...
}

Next, install gh-pages.

npm install --save-dev gh-pages

Add the following scripts to package.json:

//rainy-day-fund/package.json
{
//...
"scripts":{
//...
"predeploy": "npm run build",
"deploy": "gh-pages -d build"
}
}

Now deploy the app.

npm run deploy

Finally, check that your project uses gh-pages in the settings tab of your project’s repo.

Head over to the homepage URL that you specified, and you should see the Rainy Day Fund App!

That’s a wrap

Please comment if you find errors or any other issues with this tutorial. And congrats on getting this far!