While Stateless Function Components (SFCs) are a handy tool in your arsenal, ES6 Class Components are still the de-facto way to write React components that utilizes state or lifecycle hooks.
A hypothetical ES6 Class Component might look something like this (over-simplified without error checking, of course).
class Foo extends Component {constructor(props) {super(props);this.state = { loading: true };}
async componentDidMount() {const data = await loadStuff();this.setState({ loading: false, data });}
render() {const { loading, data } = this.state;return ({loading ? <Loading /> : <View {...data} />});}}
We initialize our state
in the constructor
, asynchronously load our data in componentDidMount
, and render our View
component based on the loading
state. A pretty standard pattern — at least for me, if you’ve been following my work.
We’ve all been taught that the constructor
is where we initialize our instance properties, state
in this case. And if you are saying to yourself, “Exactly!”, then you would be absolutely correct… if not for the upcoming ES.next class properties proposal, currently in stage 3.
With it we can now define class properties directly, like this.
class Foo extends Component {state = { loading: true };...}
Babel will transpile your code and add a constructor
for you behind the scenes. Here is the output from Babel when we transpile the code snippet above.
Note that Babel is actually passing all args — not just props
— down to super
. It is also taking super
’s return value and passing it back to the caller. Both may be a bit overkill, but exactly what it should be doing.
There’s still a constructor, you just don’t see it.
Another reason that we’re taught to use the constructor
is for binding methods to this
, like so.
class Foo extends Component {constructor(props) {super(props);this.myHandler = this.myHandler.bind(this);}
myHandler() {// some code here that references this}...}
Some people ignore this all together by assigning a function expression to a class property, but that’s a different story all-together. Read more about this in my other ES6 React Classes article Demystifying Memory Usage using ES6 React Classes.
Demystifying Memory Usage using ES6 React Classes_Which is more efficient? Binding in the constructor, or using an arrow function as a class property?_medium.com
Let’s assume for a moment that you are in the bind
camp (and even if you’re not, bear with me). We’ll need to bind in the constructor
, right? Not necessarily. We can do the same thing that we did for class properties above.
class Foo extends Component {myHandler = this.myHandler.bind(this);
myHandler() {// some code here that references this}...}
What about when you need to derive your initial state
from props
, say for initializing a default value? Surely we need the constructor
for that?
class Foo extends Component {constructor(props) {super(props);this.state = {color: this.props.initialColor};}
render() {const { color } = this.state;return (<div>{color}</div>);}}
Nope! Again, class properties to the rescue! We have access to both this
and props
.
class Foo extends Component {state = {color: this.props.initialColor};...}
Maybe we need a constructor
to fetch data? Hardly. As we saw in our first code sample, any data loading should be done in componentDidMount
. But why componentDidMount
? We do it there so that the fetch isn’t performed when running the component on the server — as is the case when doing Server Side Rendering (SSR) — as componentDidMount
is not performed server side.
We’ve seen that for setting our initial state
, we no longer need a constructor
(or any other instance property for that matter). We also don’t need it for binding methods to this
. Same for setting initial state
from props
. And we would most definitely never fetch data in the constructor
.
Why then would we ever need the constructor
in a React component?
Well… you don’t.
[However… If you find some obscure use case where you need to initialize something in a component, both client-side and server-side, you still have an out. There’s always _componentWillMount_
. Internally, React calls this hook right after “newing” the class (which calls the _constructor_
) on both the client and the server.]
So I maintain that for React components: The constructor is dead, long live the constructor!
Well, not reading, but Kent C. Dodds has a nice video tutorial on class properties that you may want to read, um… watch.
You can also read a bunch of React based articles (including some on Hooks in React 16.7, {…❤️} Spread Love, and React Best Practices) on the AmericanExpress.io Technology Blog.
I also write for the American Express Engineering Blog. Check out my other works and the works of my talented co-workers at AmericanExpress.io. You can also follow me on Twitter.