Writing Money Transfers in React — Part 4

Zac Tolley
Scropt
Published in
10 min readApr 23, 2018

Time to introduce state and let the user change the money transfer values. You could do this in plain old React, and many do, but Redux is a framework that separates out responsibility for managing data, more specifically state. If you haven’t already looked at the Redux Site then head over and read it’s explanation….. see, clear? No I didn’t think so either!

As usual, the code for this is available on Github.

When react first came out a tool came out for it called ‘Flux’. Flux was the goto tool to handle state and it’s ideas were good, it just was not too great to use and felt like there was too much scaffolding and boilerplate code. Since then a couple of other flavours have emerged but the one that caught my eye, and seems to be the most popular, is Redux.

Like Flux it has the idea that data travels in one direction.

The whole thing starts off with a component invoking an action creator, this in turn creates an action. The action is passed onto something called a ‘Reducer’, maybe they got the name from map reducers, I don’t know. A Reducer takes the previous state, the action and returns a new state. The new state is passed onto the component as properties and causes a re-render and the cycle starts again.

The best way to learn how to use Redux is use it. Lets start off using it to get the list of transfers to show on the screen. Before we do anything with the components we need to create a Reducer and an Action Creator.

Action Creator

When I create actions I like to organise related ones in a file and put it in a folder named ‘actions’. Lets create a transfers_actions.js file:

export const GET_TRANSFERS = 'GET_TRANSFERS';var transferData = [
...
];
export function getTransfers() {
return {
type: GET_TRANSFERS,
payload: transferData,
}
}

An action creator simply returns an object with a type and a payload, thats it. Hardly rocket science and for the majority of action creators it really doesn’t get much more complicated than that. Right now this just returns hard coded data which is not very exciting but you’ll see.

Reducer

When writing reducers you first figure out the list of top level properties your app needs and create a Root reducer, this is simply a top level key value pair that contains all the other reducers.

So, create a ‘reducers’ folder and create a root reducer in index.js

import { combineReducers } from 'redux';
import TransfersReducer from './transfers_reducer';
export default const rootReducer = combineReducers({
transfers: TransfersReducer,
});

I’m not going to explain how it all works, it’s just boiler plate you cut and paste. Next create transfers_reducer.js

export default function(state = [], action) {
switch (action.type) {
case GET_TRANSFERS:
return action.payload;
}
return state;
}

All actions get sent to all reducers. The job of a reducer is to detect actions it is interested in and create a new state based on the existing state and the action payload. Existing state for the reducer is passed in as a parameter and ES6 lets us say if there is no value for the existing state parameter then give it a default, the very first initial state. Note that you don’t mutate the existing state, you create a brand new state, so be careful to not just say newstate = oldstate, you have to make sure you copy the data to a new state object and then change it.

In this code if the reducer receives a GET_TRANSFERS action then it just says the new state is the contents of the action payload, or in other words it makes the current value of transfers equal to the transfers passed to it.

Integration

So now you want to tell MoneyTransfers that it should use Redux to get transfers and use them when it renders it’s list. When you use Redux actions and properties you tell your component to ‘connect’ actions and state to the components properties.

import React, { Component } from 'react';
import { connect } from 'react-redux';
import { getTransfers } from '../actions/transfers_actions';
import MoneyTransfer from './money-transfer';
class MoneyTransfers extends Component { componentWillMount() {
this.props.getTransfers();
}
renderTransfer = (transfer, index) => {
return (
<MoneyTransfer transfer={transfer} key={index}/>
);
}
render() {
return (
<div>
<ul id="transfers" className="card-list">
{this.props.transfers.map(this.renderTransfer)}
</ul>
</div>
);
}
}
MoneyTransfers.propTypes = {
transfers: React.PropTypes.array,
getTransfers: React.PropTypes.func,
};
function mapStateToProps({ transfers }) {
return { transfers };
}
export default connect(mapStateToProps, { getTransfers })(MoneyTransfers);

The changes are subtle, and the main bit you’ll notice is we define a mapping of reducers state to component properties and use the connect statement to join it all together and produce the object that should be exported, instead of the raw class. You’ll see this boiler plate used on Redux apps, it’s not pretty but it’s what it takes to join things up.

Store

So there’s just One more thing left to do (honestly). The core of Redux is the ‘Store’. It co-ordinates actions, reducers, components and more. You must create a store, linked to your reducers and then wrap your application in it. Make sense? Again, just boilerplate you do once. So money.js should now look like this:

import React from 'react';
import ReactDOM from 'react-dom';
import { Provider } from 'react-redux';
import { createStore } from 'redux';
import MoneyTransfers from './components/money-transfers';
import reducers from './reducers';
ReactDOM.render(
<Provider store={createStore(reducers)}>
<MoneyTransfers />
</Provider>,
document.querySelector('#money-transfers-container')
);

Hurrah, we are using Redux to request the transfers, this populates the applications transfers property and we see them on screen. So, lets allow people to update transfers.

Who does what?

For years now, Model View Controller was THE way of doing things but front-end frameworks don’t fit into that. Angular sort of pretends to be MVC, or as some people call it Model View Whatever, but React and Redux embrace their difference. React components are clearly Views, but actions and reducers don’t fit MVC. The only phrase I could come up with was Action State ViewComponent:

  • Action: This layer Does something. It makes simple decisions and goes and does stuff, such as getting data (more later).
  • State: The state layer is responsible for taking the result of an action and applying it to the application state.
  • ViewComponent: A component, or View Component, in this analogy really is a mix of view and controller. The render method is the view and the rest of the component is a lightweight view controller.

If I have a component that renders a transfer and the user changes the account from I could implement an action called ‘UpdateAccountFrom’ which would take the transfer and the new account from OR have an ‘UpdateTransfer’ action that takes a transfer. I opted for the latter, otherwise if your object has many properties it will get messy, so I chose to make my components act like a mix of View and Controller.

When I change the Account From the Transfer Account and Account List work together to allow a person to pick an account and pass this change back. Back in Part 3 we created a method in TransferAccount that just outputted to the console when a person selected an account.

Lets take the selectAccount idea and take it one more level up the chain to MoneyTransfer

In MoneyTransfer we could tell it bind to Redux and call the UpdateTransfer method with an updated transfer so lets it. Update TransferAccount with:

selectAccount = (account) => {
this.setState({
open: false,
});
this.props.selectAccount(account);
}

And in MoneyTransfer:

setAccountFrom = (account) => {
let newTransfer = this.props.transfer;
newTransfer.accountFrom = account;
this.props.updateTransfer(newTransfer);
}
setAccountTo = (account) => {
let newTransfer = this.props.transfer;
newTransfer.accountTo = account;
this.props.updateTransfer(newTransfer);
}
export default connect(null, { updateTransfer })(MoneyTransfer);

You also need to edit the TransferAccount tags in MoneyTransfer to use the two new methods. After this add a new action.

export function updateTransfer(transfer) {
return {
type: UPDATE_TRANSFER,
payload: transfer,
};
}

And add a handler in the reducer to create a new state when there is an update. Note that you don’t mutate it, you create a new one. Looking at the update code I clone the existing state and replace the old transfer with the new one. I know someone will know a better way of doing this but it works.

function updateTransfer(state, transfer) {
let newState = state.slice(0);
for (let pos = 0; pos < newState.length; pos += 1) {
if (newState[pos].id === transfer.id) {
newState[pos] = transfer;
break;
}
}
return newState;
}
export default function(state = [], action) {
switch (action.type) {
case GET_TRANSFERS:
return action.payload;
case UPDATE_TRANSFER:
return updateTransfer(state, action.payload);
}
return state;
}

Fire it up, change an account from the drop down and…. BAM… nothing…

I started debugging, was it calling the action… Yup. Was it getting sent to the reducer… yup. Was the reducer returning a new state object with a list of transfers with the new one… YUP! So why?

When we connect MoneyTransfer to the store we only connect the action it needs and put null in for the state to properties parameter which results in the state no longer being wired up and changes in state don’t seem to trigger a re-render. Now I’ll be the first to say that this may be me getting things wrong but it would appear that a once you have a top level object wired up to Redux for state and actions, a child object cannot be wired up. The workaround feels like a hack and I’m hoping there is a way around it.

So the workaround is to add the update action creator to MoneyTransfers and pass a reference to it down to MoneyTransfer as a property. In MoneyTransfers make these changes:

import { getTransfers, updateTransfer } from '../actions/transfers_actions';<MoneyTransfer transfer={transfer} key={index} updateTransfer={this.props.updateTransfer} />export default connect(mapStateToProps, { getTransfers, updateTransfer })(MoneyTransfers);

And remove the connect line at the end of MoneyTransfer. Refresh your browser, choose a different account and … finally. Now a bit of cleanup for the amount so back to MoneyTransfer.

setAmount = (event) => {
let newTransfer = this.props.transfer;
newTransfer.amount = event.target.value;
this.props.updateTransfer(newTransfer);
}
...<input type="text"
id="balance"
name="account[balance]"
className="form-control form-control__number"
value={transfer.amount}
onChange={this.setAmount}
/>

Now when you type in the amount field, the value changes.

Adding more and deleting.

To make this usable there’s a few more things to do. First of all, lets make it so that when you have a complete transfer, a new one is added. As the reducer is responsible for state then that is the best place this logic.

I created some utilities for handling transfer data, common things that need to be done that best live in a separate place so they can be easily tested. In the reducer update method they are called so the following adds a new blank transfer when needed:

if (!containsIncompleteTransfer(newState) || newState.length === 0) {
appendNewTransfer(newState);
}

Adding a delete action is just more of the same. First we create the action in transfer-actions.js

export function deleteTransfer(transfer) {
return {
type: DELETE_TRANSFER,
payload: transfer,
};
}

Then in the reducer:

function deleteTransfer(state, id) {
let clonedState = state.filter(item => item.id !== id);
return clonedState;
}
...case UPDATE_TRANSFER:
return deleteTransfer(state, action.payload);

Wire the action into MoneyTransfers and pass it to MoneyTransfer. Finally, in MoneyTransfer, create a handler and assign it to the delete link.

deleteTransfer = () => {
this.props.deleteTransfer(this.props.transfer);
}
<a className="delete-button" onClick={this.deleteTransfer} >
Delete
</a>

The end result is roughly what you’ll find at Part4 in the github repo. Build and run and you’ll see you can edit transfers.

Missing Stuff

The code in these tutorial steps is not the complete thing, there is alot of ‘stuff’ that I’m glossing over because it’s not really code that helps you understand React or Redux but it is code that is needed to make the application usable. One example of what I mean is when you click the account dropdowns; it’s a bit jarring as the current selected account is not at the top. In Github there is going to be a tag just for the complete thing, that will have a whole bunch of extra little bits and pieces that make the interface a tiny bit more polished so feel free to look at that after the walk-through.

Closing notes

So looking back, what did we do? We learned that action creators create actions, which describe a thing a happen. Reducers take actions and apply them to state. Components can be wired up to both so they can receive state and references to actions creators and Redux has something called a store that ties them together. Finally you learned how introducing these roles into the project, logic and state are now managed by Redux so the React components can focus on being lightweight view/controllers. It may seem at first that this is all a bit complex and you could indeed do this without Redux but it gives structure and the one way flow of data is definitely a good pattern to follow.

Coming next

The next instalment will be the last one, we’ll hook things up to the server and talk about how to work with async network actions and the issue I had with updating to the server while keeping the interface snappy and not overloading it.

--

--