Learning About React Hooks — A Coffee Lover’s Perspective
I became interested in hooks after I read Smart and Dumb Components (I actually mostly focused on the disclaimer at the end) while learning about React and building Catoro, a coffee tasting app.
Right after I began planning Catoro, the importance of Hooks became relevant when I laid out my app flow. This is how my first draft looked:
As you can see as I was building my app. I was starting to develop a flow of having two ‘smart’ containers one for TableCuppings (TablesContainer) and another for Home related stuff (HomeContainer).
However, as I was building the different parts of the app, the logic and passing in-out props was becoming out of hand and I was starting to lose the sense of app flow (as well of myself). So back to get some more coffee and to the drawing board.
Long story short. Bring on hooks to the rescue!
What are hooks?
As you might know, React’s foundation are components, which are basically Javascript functions (written as JSX) that then render HTML elements on the screen. In the most basic sense components are thought to be either class or functional. The traditional way of distinguishing class vs functional goes as follows:
- Syntax: Functional components are basic JS functions, accepting arguments as props and returning JSX elements. Class components use ES6 class syntax, also require to extend from React.Component. Class components are more bulky and functional tend to be leaner in code length.
- State: Class components by extending from React.Component have access to state and local state management. This state is usually passed down as props to functional components, which its sole ‘function’ is to render JSX on this line of thought.
- Lifecycle Methods: Class component have access to lifecycle methods such as
componentDidMount()
andcomponentDidUpdate()
. The benefits of this means to initiate a fetch request, or to check on previous props change to initiate other requests or do state management.
This distinction has changed. With hooks, points #2 and #3 are no longer entirely applicable. So with the introduction of ‘hooks’ like useState
and useEffect
it makes this distinction less rigid and now more a matter of choice and style.
How hooks caffeinated Catoro
While I didn’t end up getting rid of my class containers entirely, I ended up using a mixture of both approaches. However, hooks such asuseState
really helped my functional components be more efficient. This was the case withEditTable
.
Here’s an example of how EditProfile ‘used’ state:
export default function EditTable({table}) {
.. const [name, setName] = useState( table.name || "");(...)
//form Name input
<Form.Group controlId="formBasicTextF2" > <Form.Label>Cupping Table Name</Form.Label> <Form.Control type="text" value={name} onChange={e => setName(e.target.value)}
...etc
You might see from this focused example from Catoro source code, how useState was used in const [name, setName] = useState( table.name || “”)
and onChange={e => setName(e.target.value)}.
useState
requires us to define both a variable and a setter when we ‘useState’. In this case, name
was added to state as either name being passed from table
or an empty string. Then on our onChange
, we passed it directly to the setName
setter and boom. Our state is easily updated.
Catoro used React Form Hook, a form helper which uses extensively (as its name implies) hooks.
My CuppingScoreForm
functional component took this to the next level and usedState on steroids. By the use of Form Hook helper, it was able to achieve user validation, errorHandling and getting data sanitized for submission all by usingState.
More hooks to the rescue
There are other very useful implementations of hooks such as useEffect
, useReducer
, and useContext
. I will briefly explain them here for informational purposes.
useEffect
kind of implements lifecycle methods componentDidMount()
and componentDidUpdate()
within the same function. useEffect
also uses two arguments, the first one is invoked after the render, and the second, an optional array, when the effect should be called.
There’s a neat article that you can read to see useEffect
in action. In Catoro, for a brief time, I implemented the article’s example to detect a click outside of ExportPreview
. However, in the end, this functional model ended up being rendered in a separate window.
Nonetheless, to get a sense of how useState
works, this is how my code looked when I implemented useEffect
to handle a click outside (bonus point it uses useRef
):
import {useRef, useEffect} from "react";const ExportPreview = () => { const node = useRef(); const handleClick = e => { if (node.current.contains(e.target)) { // inside click, you can have a function but I returned return; } // outside click toggleExportPreview() };useEffect(() => { // add when mounted document.addEventListener("mousedown", handleClick); // return function to be called when unmounted return () => { document.removeEventListener("mousedown", handleClick); };});return (<Container className="exportPreview"
//this is how you'll ref the node. ref={node}>...etc
I think with these two examples, you’re now getting the hang of React hooks! So you might get a sense of whatuseReducer
, and useContext
could be used for.
useReducer
also takes two arguments, and in this case, the second argument is a dispatch function. With that in mind, you could do something along these lines:
const likesHandler = (props) => {
const [likesState, dispatchLikes] = useReducer(reducer, { likes: 0 })
return (
<>
<h1>Liker</h2>
<Button
onClick={() => dispatchLikes({type: 'INCREASE_LIKES', payload: 1}) }
...etc
useContext
is another basic hook. It basically lets you avoid to have to send down state by now accepting a context object.
I will let you read this one for yourself! You can check the React Hooks API Documentation. It will also give you a good extended overview of all the hooks explained so far.
Hope this made your understanding of React Hooks a bit better. Now get back to coding! I’d love to see what you guys are doing with hooks.
For me back to another cup of coffee.