React Hooks

Content

  • Why use React Hooks
  • What is a hook
  • When should I use Hook
  • Rules of hooks
  • Hooks API
  • Tips for hooks

Why use Hooks?

Current Problems

  • It’s hard to reuse stateful logic between components
  • Complex components become hard to understand
  • Classes confuse both people and machines

Hooks let you

  • Reuse stateful logic without changing your component hierarchy
  •  
  • Split one component into smaller functions based on what pieces are related
  •  
  • Use more of React’s features without classes

What is a Hook?

import React, { useState } from 'react';

function Example() {
    const [state, setState] = useState({});
    // ...
}

When should I use Hook?

if you write function components and realise that you need to add state

Rules of Hook

  • Only Call Hooks at the Top Level
  • Only Call Hooks from React Functions

Why order matter

// first render
useState('Mary')           // 1 Initialize 
useEffect(persistForm)     // 2
useState('Poppins')        // 3
useEffect(updateTitle)     // 4 


// second render
useState('Mary')           // 1 Read
useEffect(persistForm)     // 2
useState('Poppins')        // 3
useEffect(updateTitle)     // 4 

React relies on the order in which Hooks are called

useState('Mary')           // 1. Read the name state
// useEffect(persistForm)  // 🔴 This Hook was skipped!
useState('Poppins')        // 🔴 2 (but was 3).
useEffect(updateTitle)     // 🔴 3 (but was 4). 




  if (name !== '') {
    useEffect(function persistForm() {
      //...
    });
  }

Hooks API

  • useState
  • useEffect
  • useCallback
  • useMemo
  • useRef

useState

function Counter({initialCount}) {
  const [count, setCount] = useState(()=>initialCount);
  return (
    <>
      <button onClick={
() => setCount(initialCount)}> Reset</button>
      <button onClick={
() => setCount(prevCount => prevCount + 1)}>Plus</button>
    </>
  );
}

useEffect

useEffect(() => {
  const subscription = props.source.subscribe();
  return () => {
    // Clean up the subscription
    subscription.unsubscribe();
  };
});

More about useEffect

  • Mutations, subscriptions, timers, logging, and other side effects
  • effects run after every completed render
  • useEffect fires after layout and paint, during a deferred event.
  • useLayoutEffect fires synchronously

useCallback

const memoizedCallback = useCallback(
  () => {
    doSomething(a, b);
  },
  [a, b],
);

useMemo

const memoizedValue = useMemo(() =>
 computeExpensiveValue(a, b), 
[a, b]);

useRef

function TextInputWithFocusButton() {
  const inputEl = useRef(null);
  const onButtonClick = () => {
    inputEl.current.focus();
  };
  return (
    <>
      <input ref={inputEl} type="text" />
      <button onClick={onButtonClick}>
        Focus the input</button>
    </>
  );
}

Tips for Hook

Should I use one or many state variables

function Box() {
  const [state, setState] = useState({
     left: 0, top: 0,
     width: 100, height: 100 });
}
//spreading ensures we don't lose data
setState(state => ({ ...state, left: 10, top: 10 }));
// Recommended way
function Box() {
  const [position, setPosition] = useState({ 
    left: 0, top: 0 });
  const [size, setSize] = useState({
     width: 100, height: 100 });
}

How to skip effect on updates?

useEffect(() => {
    fetchData();
  }, []);

How to abort data fetch in effect hook?

useEffect(() => {
  let ignore = false;
  async function fetchProduct() {
   const response = await fetch('http://myapi/product/1');
   const json = await response.json();
   if (!ignore) setProduct(json);
   }
   return () => { ignore = true };
  }, []);

Performance optimisations

How do I implement shouldComponentUpdate?

const Button = React.memo((props) => {
  // your component
});

Are Hooks slow because of creating functions in render?

Solution

  1. The useCallback Hook lets you keep the same callback reference between re-renders

     

const memoizedCallback = useCallback(() => {
  doSomething(a, b);
}, [a, b]);

Solution

2. useMemo Hook makes it easier to control when individual children update, reducing the need for pure components.

 

function Parent({ a, b }) {
 const child1 = useMemo(() => <Child1 a={a} />, [a]);
 const child2 = useMemo(() => <Child2 b={b} />, [b]);
 return (
    <>
      {child1}
      {child2}
    </>
  )
}

Solution

3. useReducer Hook reduces the need to pass callbacks deeply

 

const TodosDispatch = React.createContext(null);
function TodosApp() {
  const [todos, dispatch] = useReducer(todosReducer);
  return (
    <TodosDispatch.Provider value={dispatch}>
      <DeepTree todos={todos} />
    </TodosDispatch.Provider>
  );
}

Prevent rerender with memo and useContext hook

  1.  (Preferred): Split contexts that don't change together
  2.  Split your component in two, put memo in between
  3. One component with useMemo inside

React Hooks

By jingliu

React Hooks

  • 283