React for Data Visualization
Student Login
  • Introduction

React Hooks 101

React Hooks are JavaScript functions. Any function can be a hook.

Hooks exist to:

  • help you write less code for little bits of logic
  • help you move logic out of your components
  • help you reuse logic between components

Some say it removes complexity around the this keyword in JavaScript and I'm not sure that it does. Yes, there's no more this because everything is a function, that's true. But you replace that with passing a lot of function arguments around and being careful about function scope.

My favorite React Hooks magic trick is that you can and should write your own. Reusable or not, a custom hook almost always cleans up your code.

Oh and good news! Hooks are backwards compatible. You can write new stuff with hooks and keep your old stuff unchanged. 👌

In this section we're going to look at the core React hooks and talk about the most important hooks for dataviz.

useState, useEffect, and useContext

React comes with a bunch of basic and advanced hooks. The core hooks are:

  • useState for managing state
  • useEffect for side-effects
  • useContext for React's context API

Here's how to think about them in a nutshell 👇

useState

The useState hook replaces pairs of state getters and setters.

class myComponent extends React.Component {
state = {
value: "default",
}
handleChange = (e) =>
this.setState({
value: e.target.value,
})
render() {
const { value } = this.state
return <input value={value} onChange={handleChange} />
}
}

👇

const myComponent = () => {
const [value, setValue] = useState("default")
const handleChange = (e) => setValue(e.target.value)
return <input value={value} onChange={handleChange} />
}

Less code to write and understand.

In a class component you:

  • set a default value
  • create an onChange callback that fires setState
  • read value from state before rendering etc.

Without modern fat arrow syntax you might run into trouble with binds.

The hook approach moves that boilerplate to React's plate. You call useState. It takes a default value and returns a getter and a setter.

You call that setter in your change handler.

Behind the scenes React subscribes your component to that change. Your component re-renders.

useEffect

useEffect replaces the componentDidMount, componentDidUpdate, shouldComponentUpdate, componentWillUnmount quadfecta. It's like a trifecta, but four.

Say you want a side-effect when your component updates, like make an API call. Gotta run it on mount and update. Want to subscribe to a DOM event? Gotta unsubscribe on unmount.

Wanna do all this only when certain props change? Gotta check for that.

Class:

class myComp extends Component {
state = {
value: 'default'
}
handleChange = (e) => this.setState({
value: e.target.value
})
saveValue = () => fetch('/my/endpoint', {
method: 'POST'
body: this.state.value
})
componentDidMount() {
this.saveValue();
}
componentDidUpdate(prevProps, prevState) {
if (prevState.value !== this.state.value) {
this.saveValue()
}
}
render() {
const { value } = this.state;
return <input value={value} onChange={handleChange} />
}
}

👇

const myComponent = () => {
const [value, setValue] = useState('default');
const handleChange = (e) => setValue(e.target.value)
const saveValue = () => fetch('/my/endpoint', {
method: 'POST'
body: value
})
useEffect(saveValue, [value]);
return <input value={value} onChange={handleChange} />
}

So much less code!

useEffect runs your function on componentDidMount and componentDidUpdate. And that second argument, the [value] part, tells it to run only when value changes.

No need to double check with a conditional. If your effect updates the component itself through a state setter, the second argument acts as a shouldComponentUpdate of sorts.

When you return a method from useEffect, it acts as a componentWillUnmount. Listening to, say, your mouse position looks like this:

const [mouseX, setMouseX] = useState()
const handleMouse = (e) => setMouseX(e.screenX)
useEffect(() => {
window.addEventListener("mousemove", handleMouse)
return () => window.removeEventListener(handleMouse)
})

Neat 👌

useContext

useContext cleans up your render prop callbacky hell.

const SomeContext = React.createContext()
// ...
<SomeContext.Consumer>
{state => ...}
</SomeContext.Consumer>

👇

const state = useContext(SomeContext)

Context state becomes just a value in your function. React auto subscribes to all updates.

And those are the core hooks. useState, useEffect, and useContext. You can use them to build almost everything. 👌

useRef

You can use useRef for any state that doesn't change, but it shines most when used to reference DOM elements.

We'll use this in blackbox components and you'll see how it works there.

useMemo

useMemo behaves a lot like useEffect and is useful for memoizing large computation. It keeps the result of a function call in a local constant and only calls the function when certain values change.

Perfect for improving dataviz performance.

Created by Swizec with ❤️