Hook rules and custom hooks

Keywords: Javascript Front-end React

Hook rule

Use Hook only at the top
  • Do not call Hook in loops, conditions, or nested functions to ensure that they are always invoked at the top level of your React function. By following this rule, you can ensure that hooks are called in the same order in each rendering. This allows react to maintain the correct hook state between multiple useState and useEffect calls
Call Hook only in React function
  • Do not call Hook in the ordinary JavaScript function. You can:

✅ Calling Hook in the function component of React
✅ Call other Hook in custom Hook

How does React know which state corresponds to which useState?
The answer is that React depends on the order of Hook calls.

// ------------
// First render 
// ------------
useState('Mary')           // 1. Use 'Mary' to initialize the state variable named name
useEffect(persistForm)     // 2. Add effect to save the form operation
useState('Poppins')        // 3. Use 'Poppins' to initialize the state with the variable name surname
useEffect(updateTitle)     // 4. Add effect to update the title

// -------------
// Secondary rendering
// -------------
useState('Mary')           // 1. Read the state of the variable name name (the parameter is ignored)
useEffect(persistForm)     // 2. Replace the effect of the saved form
useState('Poppins')        // 3. Read the state of the variable named surname (the parameter is ignored)
useEffect(updateTitle)     // 4. Replace the effect of the updated title

// ...

This is why Hook needs to be called at the top level of our component. If we want to execute an effect conditionally, we can put the judgment inside the Hook:

 useEffect(function persistForm() {
    // 👍  Place conditional judgment in effect
    if (name !== '') {
      localStorage.setItem('formData', name);
    }
  });

Customize Hook

By customizing the Hook, you can extract component logic into reusable functions

Custom Hook is a function whose name starts with use. Other hooks can be called inside the function

  • Define 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;
}
  • Use custom Hook
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>
  );
}

Must the custom Hook start with "use"?
It must be. This agreement is very important.
If you don't follow it, because you can't judge whether a function contains a call to its internal Hook, React will not be able to automatically check whether your Hook violates the Hook rules.

Will using the same Hook in both components share the state?
can't. Custom Hook is a mechanism for reusing state logic (for example, setting it as a subscription and storing the current value), so every time you use a custom Hook, all States and side effects are completely isolated.

How can a custom Hook get an independent state?
Each time Hook is called, it gets an independent state. Since we directly call useFriendStatus, from the perspective of React, our component only calls useState and useEffect.
As we learned in the previous chapters, we can call useState and useEffect multiple times in a component, which are completely independent.

Posted by SyWill on Sat, 04 Dec 2021 18:57:34 -0800