The road to React grok has been a long and arduous one and with all this deep understanding we’ve been gaining over the last couple of tutorials my mind and soul grow weary. Whilst we’ve seen a number of things along the way like ES6 spells and battled fierce pattern beasts like container and presentation components. I think it’s time to take a moment to slow down and take stock of some of the things we’ve learned.

This week we’re going to take the knowledge we gained from my post about container components and presentation components and see where we can apply some of the patterns and syntax we’ve learned in our Scroll creator app. Hopefully this will mean that our Scroll generator code base will be more maintainable for use on the journey ahead.

Just before you get going though, if you haven’t been following along with the tutorials it might be beneficial for you to have a quick scan over the code base to get an idea of the structure we’re starting with and maybe even run an npm start command from the command line in the root of the project directory so you can take a look at the application running as it is now.

Want to follow along but haven’t done the previous tutorials? Clone down this repo to give you a starting point. Once you’ve cloned it down run the command npm install in the root of the project and you’ll be good to go!

Component Chop Sui!

Let’s get you up to speed. In previous tutorials, we built a ScrollCreatorForm component which then went on to house the entirety of our UI! Now that we know about the container and presentation components pattern we can flex our React muscle and refactor this component into a more componentised UI.

Remember, the goal here is to try and separate the logic which concerns itself with maintaining and manipulating state from the code whose job it is to display that state to the user. In light of this why don’t we start with some low hanging fruit?

Take a look at the bottom of the render function of the ScrollCreatorForm. There we have a panel below the data capture form. At the moment that panel is purely concerned with displaying the JSON of the underlying data structure to us for debugging purposes. As a first step towards prettying up this code, let’s refactor this out into it’s own presentation component.

Go ahead and make yourself a new component in the script folder. Call it ScrollPrintPreview. You can see the code for ScrollPrintPreview below:

import React from 'react';
import CodeQuest from './CodeQuest';
const ScrollPrintPreview = (props) => {
const {formData} = props;
return (
<div>
<strong>Print Preview</strong>
<CodeQuest
{...formData}
/>
</div>
);
};
export default ScrollPrintPreview;

Let’s break down Some of the cooler things that have gone on as part of this refactor. First, you can see that on line 10 when we define the CodeQuest component, this is a component that we built in previous tutorials that had a single field which could change the name printed after the word “Hello” on our scrolls. As part of this refactor we’re going change this component to work with our new expanded form functionality.

Upon taking a closer look at the definition of the CodeQuest component you’ll notice that we are using a strange syntax to operate on the formData constant. This syntax is another funky feature which I, unfortunately, ran out of time to explain in the ES2015 tutorial.

It’s called the spread operator and I think it’s really cool. The operator itself is the ... and in essence what it does is expand out all the properties on the object that you put after it.

So here we take the formData object that gets passed to us via props and, by using the spread operator, each of formData‘s properties gets passed down to the CodeQuest component. In reality, the CodeQuest component is only going to take a finite number of props and I could have easily typed them out individually, but one of the reasons I like the spread operator for the way it keeps the code compact and that’s what we’re gaining by using it here.

There is also an added bonus that if I want to change the schema of formData I can do so freely, safe in the knowledge that I only need to make changes in two places 1. The component in which formData is defined, and 2. In the CodeQuest component; and because we are using the spread operator to broker these props between the components this schema change can happen without ever having to even look at the source for ScrollPrintPreview.

Making the old, new again

Next we should refactor the CodeQuest component to take the new props that the ScrollPrintPreview is now going to be sending it. While we’re at it we can and also bring it into line with the best practices that we now know from previous tutorials.

I know you’re going to be impressed when you see just how much of a differences these patterns can make. To emphasis the point I’ll line up the way that the code used to look compared to how we want it to look.

So, here’s what the CodeQuest component currently looks like:

import React, { Component } from 'react';
class CodeQuest extends Component {
constructor(props) {
super(props);
this.state = { name: 'Friend' };
this.handleChange = this.handleChange.bind(this);
this.scrollData = [
'I\'d like to invite you on a wondrous adventure towards \r\n the grok of ReactJs. Will you join me?',
'Zac Braddy',
'The Reactionary',
];
}
render() {
const { handleChange, scrollData } = this;
const { name } = this.state;
return (
<div>
<input type="text" onChange={handleChange} />
<div className="scroll">
<div>Hello {name}</div>
{
scrollData.map(textItem => {
return (<div>{textItem}</div>);
})
}
</div>
</div>
);
}
handleChange(e) {
this.setState({name: e.target.value});
}
}
export default CodeQuest;

And here is what you need to replace it with:

import React from 'react';
const CodeQuest = (props) => {
const { Their_Name, Message_Body, Your_Name } = props;
let id = -1;
return (
<div>
<div className="scroll">
<div className="greeting">Hello {Their_Name}</div>
{
Message_Body.map(textItem => {
id++;
return (<div key={id} className="message-body">{textItem}</div>);
})
}
<div className="closing">
<div>{Your_Name}</div>
<div>The Reactionary</div>
</div>
</div>
</div>
);
};
export default CodeQuest;

A lot’s changed here so let’s begin at the top and work our way down. First, we are no longer destructuring Component off the imported React object on line 1. We were only using this for extending our component’s class so that react could work with it. Now there’s no class to extend and React is smart enough to know what it needs to do when you send it a pure function, thus we don’t need Component.

Next is the definition of our component as an SFC on line 3. The function that is defining our component takes props as it’s only argument. Previously the definition of the constructor came next. But because our component is no longer a class we will obviously get rid of that, along with everything inside of it.

That’s ok though because our component is now stateless and before the constructor was only concerning itself with intialising state, binding change handlers whose job it was to operate on state and defining a data structure used to display the messages on the scroll. Well, all of these things are are now getting passed in our props, so it’s bye bye constructor and change handlers!

The rest of the differences aren’t really all that mind blowing because once we’ve removed the constructor and the change handler function all that is left in the class is the render method and when you use an SFC react assumes that the function itself is the render method. There are only very superficial changes inside the code that was formerly the render method. All of these changes basically centre around setting up the component to display the formData object that is passed in to our new component rather than the scrollData that was formerly being used.

So now that we have reintroduced our original codeQuest component let’s take a moment and quickly update the css for it so that it fits nice and neatly into our refactored UI. Nothing in the code is really what you’d describe as “state-of-the-art CSS” so a short google search will get you all the information you need on this if find that you’re a combination of both interested and stuck. If you aren’t either of these things then I’d just copy paste this code over the old file:

.scroll {
width: 250px;
min-height: 325px;
background-image: url('../assets/scroll.gif');
background-repeat: repeat-y;
}
.scroll .message-body {
padding: 0 20px 0 40px;
word-wrap: break-word;
}
.scroll .greeting {
padding: 70px 0 20px 30px;
}
.scroll .closing {
padding: 20px 0 0 30px;
}

Now that we have our new component defined, it’s time to refactor the ScrollCreatorForm to use it. Below is the code you’re looking for in the ScrollCreatorForm that you want to edit:

<div className="scrollCreatorPanel">
<strong>Output</strong>
<pre>{JSON.stringify(this.state, null, 2)}</pre>
</div>

And here is what you should have after the change:

<div className="scrollCreatorPanel">
<ScrollPrintPreview
formData={this.state}
/>
</div>

Lastly you’ll need to add an import statement to bring the ScrollPrintPreview element into the ScrollCreatorForm. At this point of the tutorial series I feel confident that you will be able to do this without a code snippet but feel free to consult the completed github repo branch for this lesson if you have any problems.

Set phasers to….stunned amazement

Your code should now be ready for you to execute an npm start command at the root of your project in the command line and you should find that when you do the application should launch in all it’s glory! You’ll see that the form we created in Jazzing up data capture still works the way it did before but now it is live updating our CodeQuest component that we are using for previewing the finished scrolls. How exciting! After a short play around you’ll notice that the multi-page feature for when the messages get too long leave a bit to be desired but let’s worry about that in v2!

Let’s quickly recap what we’ve done. We identified that the Print Preview section of the application was essentially only concerned with displaying the state as it exists at a point in time. We decided that this functionality was significantly different enough from the concerns of the ScrollCreatorForm itself to justify us splitting it out into it’s own component.

So we refactored this part of the render function out of the ScrollCreatorForm component and put it in it’s very own stateless functional component whose job it was to take care of displaying the print preview section of the screen. Then within this print preview section we gave it a CodeQuest component which we also refactored into an SFC. We used this component to take care of the print preview itself.

Kicking it up a notch!

So at this point we have parts of the system looking nice but that hasn’t done a lot to reduce the size and structure of our ScrollCreatorForm. It’s still 130 odd lines long!

Now that you’ve seen the process we go through when looking for opportunities to refactor and you’re familiar with the concept of container and presentation component, take a look at what we have left in ScrollCreatorForm code. What can you see that you think is a good candidate for pulling out next? Go ahead, take a look, I’ll wait.

If you noticed the two functions renderSingle and renderMulti and thought, “They basically already look like SFCs!” you’d be right. Let refactor these into their own components. We’ll start with the renderSingle method. We’ll refactor this into a component which we will call TextInput because, after all, that is what it actually is right?

import React from 'react';
const TextInput = (props) => {
const { id, fieldName, fieldValue, onChange } = props;
const humanReadableFieldName = fieldName.replace('_', ' ');
return (
<div key={id} className="form-group">
<label htmlFor={`${fieldName}${id}`}>{humanReadableFieldName}</label>
<input
id={`${fieldName}`}
type="text"
className="form-control"
placeholder={humanReadableFieldName}
value={fieldValue}
onChange={onChange}
/>
</div>
);
};
export default TextInput;

Then we can make another component to replace renderMulti. Let’s get creative with the name on this one. I want to make it something jazzy like…MultiTextInput. Yeah!

import React from 'react';
const MultiTextInput = (props) => {
const {id, fieldName, fieldValue, onChange, addOnClick, removeOnClick} = props;
let subId = -1;
const humanReadableFieldName = fieldName.replace('_', ' ');
return (
<div key={`${id}`} className="form-group">
<label htmlFor={`${fieldName}${id}`}>
{humanReadableFieldName}
</label>
<button className="btn btn-success btn-xs add-body-button"
onClick={() => {addOnClick(fieldName)}}>
<i className="glyphicon glyphicon-plus"/>
</button>
<div id={`${fieldName}${id}`}>
{
fieldValue.map(val => {
subId++;
console.log(fieldValue);
return (
<div className="input-group multi-field-input" key={`${fieldName}${id}-${subId}`}>
<input
id={`${fieldName}_${subId}`}
type="text"
className="form-control"
placeholder={`${humanReadableFieldName}`}
value={val}
onChange={onChange}
/>
<span className="input-group-btn">
<button className="btn btn-danger" onClick={(e) => removeOnClick(fieldName, subId)}>
<i className="glyphicon glyphicon-remove"/>
</button>
</span>
</div>
);
})
}
</div>
</div>
);
};
export default MultiTextInput;

Now for the most part these functions are unchanged from when they were functions in the ScrollCreatorForm component. The only difference is in the way we call the change and click handlers. This is because previously we getting bind called on them in line in the JSX.

But now that we have the handlers in a separate component to the JSX that is calling them binding in line in TextInput and MultiTextInput respectively wouldn’t bind the this instance to the ScrollCreatorForm class like we want it to. Let’s the explanation at that because function binding is really the topic for another post by itself.

This is why we needed to make some changes to the code used for these handlers. So, other than those few superficial changes you can see that the render functions are exactly the same apart from the fact that now they inhabit their own component rather than bloating ScrollCreatorForm. Niccceeeee.

Get ready! Because now we’re ready to head back into our ScrollCreatorForm and hook it up to use our new input component. Let’s see what the code looks like:

import React, { PropTypes, Component } from 'react';
import ScrollPrintPreview from './ScrollPrintPreview';
import TextInput from './TextInput';
import MultiTextInput from './MultiTextInput';
class ScrollCreatorForm extends Component {
constructor(props) {
super(props);
const initialState = {};
Object.keys(props.fields).forEach(field => {
if (props.fields[field] === 'single') initialState[field] = '';
if (props.fields[field] === 'multi') initialState[field] = [''];
});
this.state = Object.assign({}, initialState);
this.changeSingleValue = this.changeSingleValue.bind(this);
this.changeMultiValue = this.changeMultiValue.bind(this);
this.createNewEntryForMulti = this.createNewEntryForMulti.bind(this);
this.removeEntryForMulti = this.removeEntryForMulti.bind(this);
}
createNewEntryForMulti(fieldName) {
const newState = {};
newState[fieldName] = this.state[fieldName].concat('');
this.setState(newState);
};
removeEntryForMulti(fieldName, id) {
const newState = {};
newState[fieldName] = [];
for (let i = 0; i < this.state[fieldName].length; i++) {
if (i == id) continue;
newState[fieldName].push(this.state[fieldName][i]);
}
this.setState(newState);
}
changeSingleValue(e) {
const newState = {};
newState[e.target.id] = e.target.value;
this.setState(newState);
}
changeMultiValue(e) {
const newState = {};
const fieldNameParts = e.target.id.split('_');
let fieldName = [];
for (let i = 0; i < fieldNameParts.length - 1; i++) fieldName = fieldName.concat(fieldNameParts[i]);
fieldName = fieldName.join('_');
const subId = fieldNameParts[fieldNameParts.length - 1];
newState[fieldName] = this.state[fieldName];
newState[fieldName][subId] = e.target.value;
this.setState(newState);
}
render() {
let id = -1;
return (
<div>
<div className="scrollCreatorPanel">
{
Object.keys(this.state).map(fieldName => {
id++;
if (typeof(this.state[fieldName]) === 'string')
return (
<TextInput
id={id}
key={id}
fieldName={fieldName}
fieldValue={this.state[fieldName]}
onChange={this.changeSingleValue}
/>
);
else
return (
<MultiTextInput
id={id}
key={id}
fieldName={fieldName}
fieldValue={this.state[fieldName]}
onChange={this.changeMultiValue}
addOnClick={this.createNewEntryForMulti}
removeOnClick={this.removeEntryForMulti}
/>
);
})
}
</div>
<div className="scrollCreatorPanel">
<ScrollPrintPreview
formData={this.state}
/>
</div>
</div>
);
}
}
ScrollCreatorForm.propTypes = {
fields: PropTypes.object.isRequired,
};
export default ScrollCreatorForm;

Obviously, the first thing we’ve done here is remove the renderSingle and renderMulti functions. This would have immediately gaving us some “red squigglies”. So in response to this we replace the uses of these functions within the JSX with our new TextInput and MultiTextInput respectively.

Yet our “red squigglies” remain! So next we head up to the top of our ScrollCreatorForm function and import the components that we’ve just added (TextInput and MultiTextInput). Finally, we make some minor adjustments to the changeSingleValue and changeMultiValue functions. Again these changes are purely due to the fact that the handlers and invocations of these handlers are now happening in different components.

We are now back to a running state, if you were to execute an npm start at this point you will see the application is happily working.

Perfection at 99%

So we are very close to having a nice looking structure for our application however there is still one thing that is bothering me a little about this code. For me, the render method of ScrollCreatorForm is doing just a little too much for me to consider it to be a pure container component.

So, the last thing we will do is split out state and state manipulation into a component above the ScrollCreatorForm and leave behind only the rendering of the form. here’s what the ScrollCreatorForm looks like after we do this:

import React from 'react';
import ScrollPrintPreview from './ScrollPrintPreview';
import TextInput from './TextInput';
import MultiTextInput from './MultiTextInput';
const ScrollCreatorForm = (props) =>
{
const { formData, onChangeSingle, onChangeMulti, addOnClickMulti, removeOnClickMulti } = props;
let id = -1;
return (
<div>
<div className="scrollCreatorPanel">
{
Object.keys(formData).map(fieldName => {
id++;
if (typeof(formData[fieldName]) === 'string')
return (
<TextInput
id={id}
key={id}
fieldName={fieldName}
fieldValue={formData[fieldName]}
onChange={onChangeSingle}
/>
);
else
return (
<MultiTextInput
id={id}
key={id}
fieldName={fieldName}
fieldValue={formData[fieldName]}
onChange={onChangeMulti}
addOnClick={addOnClickMulti}
removeOnClick={removeOnClickMulti}
/>
);
})
}
</div>
<div className="scrollCreatorPanel">
<ScrollPrintPreview
formData={formData}
/>
</div>
</div>
);
};
export default ScrollCreatorForm;

It’s still a pretty big component but at least now its just a stateless functional presentation component and these are all properties that we like in a component!

There were also very few code changes necessary to achieve this result. First, we went to all the places were we were accessing this.state and instead used an appropriate local constant which we’ve defined by destructuring them off the props that are passed in. Also we renamed the variable fields to formData, this change is arbitrary but I think it better describes what we are using this data structure for now.

We’re now approaching the end of this refactoring, the last component that we need to create is the container component that will hold the state to be fed into and displayed by all our presentation components. Create a new file and call it ScrollCreator.jsx. The code for this file is below:

import React, { Component } from 'react';
import ScrollCreatorForm from './ScrollCreatorForm';
class ScrollCreator extends Component {
constructor(props) {
super(props);
const initialState = {};
Object.keys(props.fields).forEach(field => {
if (props.fields[field] === 'single') initialState[field] = '';
if (props.fields[field] === 'multi') initialState[field] = [''];
});
this.state = Object.assign({}, initialState);
this.changeSingleValue = this.changeSingleValue.bind(this);
this.changeMultiValue = this.changeMultiValue.bind(this);
this.createNewEntryForMulti = this.createNewEntryForMulti.bind(this);
this.removeEntryForMulti = this.removeEntryForMulti.bind(this);
}
createNewEntryForMulti(fieldName) {
const newState = {};
newState[fieldName] = this.state[fieldName].concat('');
this.setState(newState);
};
removeEntryForMulti(fieldName, id) {
const newState = {};
newState[fieldName] = [];
for (let i = 0; i < this.state[fieldName].length; i++) {
if (i == id) continue;
newState[fieldName].push(this.state[fieldName][i]);
}
this.setState(newState);
}
changeSingleValue(e) {
const newState = {};
newState[e.target.id] = e.target.value;
this.setState(newState);
}
changeMultiValue(e) {
const newState = {};
const fieldNameParts = e.target.id.split('_');
let fieldName = [];
for (let i = 0; i < fieldNameParts.length - 1; i++) fieldName = fieldName.concat(fieldNameParts[i]);
fieldName = fieldName.join('_');
const subId = fieldNameParts[fieldNameParts.length - 1];
newState[fieldName] = this.state[fieldName];
newState[fieldName][subId] = e.target.value;
this.setState(newState);
}
render() {
return (
<ScrollCreatorForm
formData={this.state}
onChangeSingle={this.changeSingleValue}
onChangeMulti={this.changeMultiValue}
addOnClickMulti={this.createNewEntryForMulti}
removeOnClickMulti={this.removeEntryForMulti}
/>
);
}
}
export default ScrollCreator;

You can see by the highlighted lines above that all that’s left is to add an import statement for the ScrollCreatorForm on line 2 so that you can then use the ScrollCreatorForm in the render function on line 56.

The final thing you will need to do to get this all working is head on in the index.jsx in the root of the src folder and swap any references to ScrollCreatorForm for references to ScrollCreator. The references that you need to change live on line 4 and line 24, I’m very confident that you’re capable of doing this without a code snippet so I’ll leave that out and just tell you, for the final time, that the project is now ready to run. #SingleTear

Reflections on Grok

You have now gotten through the knowledge piece on presentation components and container components as well as seeing the nuts and bolts of how we can refactor a “first draft” of a react component into something that more closely conforms with this pattern. If you’ve made it this far, I’m sure you are going to be in a great position to use this pattern in your own React creations and I wish you the best of luck doing so!

Till next time Code-venturer I hope you keep enjoying your journey on the path to Grok of the React JS library.