React Hooks II

INFO 253A: Frontend Web Architecture

Kay Ashaolu

Rules of Hooks

  • Must use hooks in functional React components
  • Must use hooks at the top level of your components, or in your custom hooks
    • This means don't use hooks inside conditionals either
    • This is because React relies on the order of hooks to determine functionality
    • If the order of hooks executed changes dynamically during execution, very hard to figure out bugs will appear

Custom Hooks

  • React provides the ability to write your own hooks
  • Custom Hooks provide another way to share stateful logic across components
  • What is this stateful logic you speak of? Or what does that even mean? 

Custom Hooks

  • As your front end application becomes more complex, it becomes harder to manage all of the state variables as well as all of the logic that modifies those state variables
  • What happens when you want to have some state that affects multiple components? 
  • React has provided a few ways of accomplishing this task, but typically involve creating more components that contain the shared state at a higher level.
  • Hooks provide an alternative path that does not necessiate these "higher order components"

Setup Code

import React, { useState, useEffect } from 'react';

function FriendStatus(props) {
  const [isOnline, setIsOnline] = useState(null);
  useEffect(() => {
    function handleStatusChange(status) {
      setIsOnline(status.isOnline);
    }
    ChatAPI.subscribeToFriendStatus(props.friend.id, handleStatusChange);
    return () => {
      ChatAPI.unsubscribeFromFriendStatus(props.friend.id, handleStatusChange);
    };
  });

  if (isOnline === null) {
    return 'Loading...';
  }
  return isOnline ? 'Online' : 'Offline';
}

Custom Hooks Example

 

  • We have a component called FriendStatus that displays "Online" if the Friend was online and "Offline" if not
  • Note the use of the useEffect hook to define what should happen when the component is created or is updated, and what should happen when it unsubscribes.

Custom Hooks Example

 

  • However imagine if we had another component, a contact list, where we wanted to highlight a person's name if they were online.
  • We would write out the logic, but it would require repeating a lot of the same code

Setup code 2

import React, { useState, useEffect } from 'react';

function FriendListItem(props) {
  const [isOnline, setIsOnline] = useState(null);
  useEffect(() => {
    function handleStatusChange(status) {
      setIsOnline(status.isOnline);
    }
    ChatAPI.subscribeToFriendStatus(props.friend.id, handleStatusChange);
    return () => {
      ChatAPI.unsubscribeFromFriendStatus(props.friend.id, handleStatusChange);
    };
  });

  return (
    <li style={{ color: isOnline ? 'green' : 'black' }}>
      {props.friend.name}
    </li>
  );
}

Refactoring?

  • In previous classes I've shown the benefits of putting common code into functions and using that function instead
  • Benefits include
    • not repeating code which can be error prone
    • once you update behavior for the shared function it is available everywhere
    • code is easier to read

Let's look at a custom hook

import { useState, useEffect } from 'react';

function useFriendStatus(friendID) {
  const [isOnline, setIsOnline] = useState(null);

  useEffect(() => {
    function handleStatusChange(status) {
      setIsOnline(status.isOnline);
    }

    ChatAPI.subscribeToFriendStatus(friendID, handleStatusChange);
    return () => {
      ChatAPI.unsubscribeFromFriendStatus(friendID, handleStatusChange);
    };
  });

  return isOnline;
}

What's happening here?

  • The amazing thing is that a custom hook is also just another function: inputs, outputs, and logic
  • You can use other hooks inside custom hooks
  • You are writing stateful logic that can be shared across components
  • You can also fully control the inputs and outputs of your hook. In this case, we pass in a friend ID and return whether that friend is online

How to use custom hook

function FriendStatus(props) {
  const isOnline = useFriendStatus(props.friend.id);

  if (isOnline === null) {
    return 'Loading...';
  }
  return isOnline ? 'Online' : 'Offline';
}
function FriendStatus(props) {
  const isOnline = useFriendStatus(props.friend.id);

  if (isOnline === null) {
    return 'Loading...';
  }
  return isOnline ? 'Online' : 'Offline';
}
function FriendListItem(props) {
  const isOnline = useFriendStatus(props.friend.id);

  return (
    <li style={{ color: isOnline ? 'green' : 'black' }}>
      {props.friend.name}
    </li>
  );
}

That was elegant

  • Wasn't it? Those two components are now using the useFriendStatus custom hook, and thus reduced a lot of repeated code. 
  • If anything were to change with the API or the handling of the API results, we could simply update the useFriendStatus hook and all components using it would be updated

Demo

Made with Slides.com