On New Years Day I sat down over ReactJS and decided to see what all the commotion was about. I primarily work on the backend and have dabbled lightly with AngularJS. Generally, my JavaScript work just falls to basic DOM manipulation using vanilla JS or jQuery. Nothing fancy, no components. ReactJS is fun.
Why is ReactJS so much fun, and popular? It is hackable, easy to manipulate, and it is solving hard problems in a new way. New solutions to hard problems bring a surge of euphoria. In the Drupal world, this is allowing us to harness Drupal as a data and content management system and not the presentation system. Not everyone uses Drupal in this way, but I have always envisioned Drupal as a great backend for any kind of application beyond normal content. The main roadblock I ended up encountering is fighting the render and form API in complex scenarios.
I have some experience in AngularJS and have fiddled with Angular (2 and then 4.) I felt more headache and disappointment when working with Angular (and AngularJS.) When learning Angular it may have been TypeScript instead of just Babel transpiling ES6. One thing I appreciated when working with ReactJS is just handling the rendering of components and not needing to also use its provided services, factories and other patterns forced by Angular.
There is a thing for everything
Since ReactJS is fun and popular, there are also tons of libraries. I was able to find solutions to many problems by just searching the NPM package repository. I do not always believe in dependency stuffing and do not encourage adding a dependency just for a small feature. But, libraries provide a great reference point for learning or quick prototyping.
I learned some tips and tricks by reviewing existing libraries. I would pick something that kind of did what I needed it to, then reviewed how to implement it on my own without the bells and whistles I did not need, or fix what I did need.
The component state
When working with your component there is the state property, which is just a JavaScript object of properties. You can use this for logic checks and provide values in your rendered component. The following example would render the text in a heading element.
import React, { Component } from 'react'
class App extends Component {
state = {
header: 'Check out this super duper rad text!',
}
render () {
return (
<h1>{this.state.header}</h1>
)
}
}
The state property can be modified directly, but it should be treated as an immutable object. Whenever you need to change a state value, use the this.setState({}) function. I did not know this at first and had some weird behaviors. If you set the state directly, it actually does get saved. But it is not reflected until some other action forces the component to re-render itself.
To change the heading text, I would run something like the following in a function
this.setState({
header: 'Mah new awesome header!',
});
State management is not recursive
Once I started to understand state and properly used this.setState, I hit a whole new roadblock. The method does a merge when setting values, but not a recursive merge. When trying to set a nested value it will cause all other nested values to be removed. Take the following state definition.
state = {
data: [],
filterParams: {
_format: 'json',
name: ''
},
}
We have a data property and then filter parameters we would pass to an API endpoint. For example sake, somewhere on the form an input updates the name to filter by. If we ran the following setState operation whenever the input changed, we'd end up losing some values.
this.setState({
filterParams: {
name: 'Widget'
}
});
After running this, our state object would look like the following.
state = {
data: [],
filterParams: {
name: ''
},
}
We lost our _format key! Luckily in JavaScript here is the Spread Operator. The spread operator is just three dots (...) allows you to expand an expression. In the following example we use ...this.state.filterParams to pass the existing values, and then any specific overrides. This allows us to maintain previous values and provide new ones.
this.setState({
filterParams: {
...this.state.filterParams,
name: 'Widget'
}
})
Updating the component state is asynchronous.
Like most things JavaScript, setting the component state is not a synchronous operation. I discovered this after playing around with the paging in the React DB Log prototype. The component state kept track of the current page. When clicking a paging button the page was incremented or decremented and then results fetched from the API endpoint.
The page was updated, but it was not fetching the proper page. That is because it was not using a callback.
// Before
this.setState({
page: this.state.page - 1
});
this.fetchLogEntries(this.state.page);
// After
this.setState({
page: this.state.page - 1
}, () => {
this.fetchLogEntries(this.state.page);
});
Chrome debugger tool is super sauce
The React team has created a Chrome browser extension that adds a React tab to the Chrome Developer Tools. This allows you to inspect the components and their state, which makes debugging a lot easier. Using that tool is how I learned the caveats in the above scenarios.
Get it here: https://chrome.google.com/webstore/detail/react-developer-tools/fmkadmapgofadopljbjfkapdkoienihi
There is a FireFox extension and standalone app that can be found at https://reactjs.org/community/debugging-tools.html
Want more? Sign up for my weekly newsletter