Stateful Functional Components in React

Undeniably, the single biggest source of confusion for new JavaScript developers is the this keyword. That’s because even though you expect this to represent the object from which it is being called, that is not always the case.

Understanding what value this takes on isn’t terribly difficult. And really the only time you have to be aware of it taking on what you don’t expect is when it’s used in a callback. So all you have to do is remember to bind this to callback functions and forget that I ever brought this up.

But, the real issue isn’t writing JavaScript code and not have it break right away. It’s reading other people’s code and being able to reason about what it’s trying to do. this is almost always going to look kind of ambiguous, even to a well seasoned eye. And if it takes up just .1% of your cranial capacity to reason about what it’s referring too, well then that’s too much. It should just be immediately obvious what a piece of code is trying to do, or at least as clear as is technically possible.

Let’s look at a very simple form in React:

class CreatePost extends React.Component {
constructor(props) {
super(props)
this.state = {
subject: "",
body: ""
};
this.handleChange = this.handleChange.bind(this);
}
  handleChange(event) {
this.setState({[event.target.name]: event.target.value});
}
  handleSubmit(event) {
//logic for saving post here
}
  componentDidMount() {
//maybe some lifecycle action here
}
  render() {
return (
<form onSubmit={this.handleSubmit}>
<label>
<input
type="text"
placeholder="Subject"
value={this.state.subject}
name="subject"
onChange={this.handleChange} />
</label>
<label>
<textarea
value={this.state.body}
name="body"
onChange={this.handleChange}>
</textarea>
</label>
</form>
)
}
}

This isn’t that bad. It’s mostly just annoying that you have to write “this.handleChange = this.handleChange.bind(this);” up in the constructor. You can alternatively write React components in a traditional way using “createReactClass” that autobinds this. But again, the point isn’t that you have to write more or less code, it’s that it can be at least a little bit more difficult to understand what the code is doing.

Alright, time for what I’ve been trying to get to: a clearer way to write React Components. Take a look:

function CreatePost(props) {
const component = new React.Component(props);
  component.state = {
subject: "",
body: ""
};
  function handleChange(event) {
component.setState({[event.target.name]: event.target.value});
}
  function handleSubmit(event) {
//logic for saving post here
}
  component.componentDidMount = function() {
//some lifecycle action here
}
  component.render = function() {
return (
<form onSubmit={handleSubmit}>
<label>
<input
type="text"
placeholder="Subject"
value={component.state.subject}
name="subject"
onChange={handleChange} />
</label>
<label>
<textarea
value={component.state.body}
name="body"
onChange={handleChange}>
</textarea>
</label>
</form>
)
}
  return component;
}

No more this and everything here works as expected. You can still use Redux or any other Higher Order Components.

What’s going on here? You’re just creating an instance React.Component inside the function, and then you’re eventually returning that instance after you’ve extended it.

The main difference, at least in my opinion, it is abundantly clear what everything is doing. Who’s state are you setting? The component’s of course. You only extend the component’s lifecycle and render methods. All your other functions are just part of the wrapping function, but still work because of the magic of closure.

If you’ve used Parasitic Inheritance, this should all be somewhat familiar to you.

Will writing React Components like this generate a 10x benefit to productivity? No. What about a 10% boost? Probably not.

Yet for larger, more complex components, it should become clearer what everything is referring to, while also helping developers who are new to React or JavaScript understand how React works. And frankly, I don’t see any compelling downsides to doing this instead.