Skip to content

React 16.8 Hooks - Part 1 #2

@taystack

Description

@taystack

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.

Metadata

Metadata

Assignees

Labels

BlogExperimental blog formatFront endFront end engineering relatedJavaScriptJavaScript related contentReact 16.8+React v16.8+ content included

Projects

No projects

Milestone

No milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions