With any programming language, framework, or library that you use, there are always common tactics that you should follow. They present an understandable, efficient way to write applications.
Beyond the short explanation that I will provide, there is more extensive documentation on this topic. The official React documentation is always a good starting point, but you can find all React patterns, including those that we have already used, at https://reactpatterns.com/.
When we wrote our post form to submit new posts or the message inputs inside chat in the previous chapters, we used controlled input by incident. To provide a better understanding, I am going to quickly explain the difference between controlled and uncontrolled components, and when to use each of them.
Let's start with uncontrolled input.
By definition, a component is uncontrolled whenever the value is not set by a property through React, but only saved and taken from the real browser DOM. The value of an input is then retrieved from a reference to the DOM Node, and is not managed and taken from React's component state.
The following code shows the post form where the user will be able to submit new posts. I have excluded the rendering logic for the complete feed, as it is not a part of the pattern that I want to show you:
import React, { Component } from 'react';
import gql from 'graphql-tag';
import { Mutation } from 'react-apollo';
const ADD_POST = gql`
mutation addPost($post : PostInput!) {
addPost(post : $post) {
id
text
user {
username
avatar
}
}
}`;
export default class Feed extends Component {
constructor(props) {
super(props);
this.textArea = React.createRef();
}
render() {
const self = this;
return (
<div className="container">
<div className="postForm">
<Mutation mutation={ADD_POST}>
{addPost => (
<form onSubmit={e => {
e.preventDefault();
addPost({ variables: { post: { text:
self.textArea.current.value } } });
}}>
<textarea ref={this.textArea} placeholder="Write your
custom post!"/>
<input type="submit" value="Submit" />
</form>
)}
</Mutation>
</div>
</div>
)
}
} In this example, you can see that we no longer have a state initializer, since the textarea value is stored within the real DOM Node, and not the application state.
Now, we need a component constructor. As we stated in Chapter 1, Preparing Your Development Environment, you always need to run the super method inside of a constructor first.
Next, we run the createRef function provided by React. It prepares the variable to accept the DOM Node as a property. In earlier versions of React, you were required to use a callback to handle this on your own. From version 16.3 of React, the createRef function automates this process for you.
In the render method, the ref property fills in the reference that we just created with the DOM element.
Accessing the value of the DOM Node works by using the normal JavaScript DOM API. You can see this behavior when sending the submit event of our form. The value is extracted from the self.textArea.current.value field.
Everything that an uncontrolled component needs is already shown here; there is no more to it. You can compare this approach to our current implementation of the post form. In our implementation, we set up the state, listen for change events, and save and read the value directly from the component state, not from the DOM element.
When using uncontrolled components and working directly with DOM elements, the problem is that you leave the normal React workflow. You are no longer able to handle conditions and, therefore, trigger other events inside of React.
Nevertheless, the DOM reference can make it easier to use third-party plugins that were not written for the React ecosystem. There are thousands of great jQuery plugins, for example. I always recommend using the default approach of a controlled component. For 99% of cases, this works without leaving the React workflow.
If you need a deeper understanding of which approach is a better solution for your specific case, take a look at https://goshakkk.name/controlled-vs-uncontrolled-inputs-react/.
One fundamental and efficient solution for writing well-structured and reusable React components is the use of stateless functions.
As you might expect, stateless functions are functions, not React components. They are not able to store any states; only properties can be used to pass and render data. Property updates are directly rerendered inside of the stateless functions, and cannot be handled by the componentWillReceiveProps method, as in React components.
We have written a lot of code where stateless functions can be used very easily; while doing so, we have also structured and improved the readability of our React application.
Beginning with the file structure, we will create a new folder for our new components (or stateless functions), as follows:
mkdir src/client/components
Many parts of our application need to be reworked. Create a new file for our first stateless function, as follows:
touch src/client/components/loading.js
Currently, we display a dull and boring Loading... message when our GraphQL requests are running. Let's change this by inserting the following code into the loading.js file:
import React from 'react';
export default ({color, size}) => {
var style = {
backgroundColor: '#6ca6fd',
width: 40,
height: 40,
};
if(typeof color !== typeof undefined) {
style.color = color;
}
if(typeof size !== typeof undefined) {
style.width = size;
style.height = size;
}
return <div className="bouncer" style={style}></div>
}
In the preceding code, we are using a simple function in ES6 arrow notation. It is an easy and more concise syntax for defining functions. In the code, you can see that we are extracting the color and size fields from the...