-
Notifications
You must be signed in to change notification settings - Fork 0
Description
React 16.8 Hooks - Part 1
The release of React 16.8 gave us a few extra tools to help optimize render speed and easily maintain state. Specifically, hooks provide a way for React to decide when the DOM needs to be updated.
You might say, "Dude, we have that already." And I would say, "correct, but not like this."
Example task:
- Create an input UI where we capitalize every word the user inputs.
Let's take a look at a very simple, stateful React pre 16.8 component.
class Example {
constructor(props) {
super(props); // boilerplate...
// initialize the state count
this.state = { text: "" };
// bind the change method
this.handleChange = this.handleChange.bind(this);
}
handleChange(event) {
const text = event.target.value.split(" ").map(capitalize).join(" ");
setState({ text });
}
render() {
return <><input onChange={this.handleChange} />{this.state.text}</>
}
}Now, let's take a look at the same component in React 16.8:
First lets make a file called useCapitalizedWords.js and put it in a folder called hooks
// hooks/useCapitalizedWords.js
import { useEffect, useState } from "react";
import { capitalize } from "helpers";
export default function useCapitalizedWords() {
// stateful text
const [text, setText] = useState("");
// When text changes
useEffect(() => {
// Update the stored text with capitalized words
setText(text.split(" ").map(capitalize).join(" "));
}, [text, setText]);
return [text, setText, text.split(" ")];
}Now all we have to do is use our hook.
// Example.js
import useCapitalizedWords from "hooks/useCapitalizedWords";
function Example() {
// [ "", (function setter), [] ]
const [text, setText, words] = useCapitalizedWords();
return (<>
<input onChange={event => setText(event.target.value)} />
{words.map(word => <div key={word}>{word}</div>)}
</>);
}I don't know about you, but I would rather maintain that last example. A simple stateful component should look as simple as it is. That was too easy. Lets try something a bit more difficult.
Example task 2 - async hook
Let's make a UI component that grabs my github avatar. We start with a fresh hook useGithubAvatar.
// hooks/useGithubAvatar.js
function useGithubAvatar(username = "taystack") {
// Store the url
const [url, setUrl] = useState("");
// Good UI always knows when a resource is loading
const [loading, setLoading] = useState(false);
// When username changes...
useEffect(() => {
// Have an async method in scope of the effect
async function getUser(username) {
setLoading(true); // Update loading boolean
// Get the user
const userResponse = await fetch(`https://api.github.com/users/${username}`);
/* You can change the next lines to:
const user = awaitUserResponse.json(); <- just capture the entire user
setUser(user); <- change [url, setUrl] = useState("") to [user, setUser] = useState({});
I would also recommend changing the name of the hook to "useGithubUser"
*/
const { avatar_url } = await userResponse.json();
setUrl(avatar_url); // Update returned URL string
setLoading(false); // Update loading boolean
}
getUser(username);
}, [username, setUrl, setLoading]); // bind our dependencies
return [loading, url]; // return the items we need from this hook
}Now when I want to get an avatar url for a user, I just invoke my custom hook! This component below will begin to appear after 100ms and gradually transition opacity over the next 100ms.
const GithubAvatar = ({ username, ...props }) => {
const [loading, src] = useGithubAvatar(username);
return (<img {...props} src={src} style={{ opacity: loading ? 0 : 1, transition: "opacity 100ms 100ms", ...props.style }} />);
}
GithubAvatar.defaultProps = { username: "taystack" };I'm leaving it here for React 16.8 Hooks Part 1
If you have any questions or complaints about the code I wrote, please leave a comment below.
I hope to inspire you into writing your own hooks. Hooks contribute to functional-style programming, which is a good thing. Like the example above shows, <GithubAvatar /> has one job - show the avatar. Even without props.username it knows what to do, explicitly. Same goes for useGithubAvatar. You can expect that hook to return an array of [bool, string]. I will create an exercise on try {} catch() {} for this hook in a later post.
Functional programming techniques like hooks allow developers to separate concerns of the view (rendered material), and the logic of how to get there. When modularized concerns are clearly defined, functional logic becomes much easier to maintain, debug, and contribute to.
