Introduce to React Hooks

What is React Hooks

Use state and other React features without writing a class

What's the problem now

Reuse stateful logic between components

  • Existing way: render props and higher order components
  • example: Media query component or windows width change or React Motion?

Wrapper Hell

Wrapper Hell

  • because we're using HOC, so we have wrapper hell

Complex components become hard to understand

  • addEventListener in two place(CDM, CWM)
  • ajaxCall in CDM and CDU
  • hard to testing, that's why you need redux but using redux introduce complexity for sharing component

Confusing classes

  • how `this` works?
  • when to use class and when to use function?
  • hard for optimize(minify not well, make hot reloading flaky and unreliable)

Hooks demo

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

export class GreetingClass extends React.Component {
  constructor(props) {
    super(props);
    this.state = {
      name: 'Bilbo Baggins',
      age: 25,
    };
    this.onHandleNameChange = this.onHandleNameChange.bind(this);
    this.onHandleAgeChange = this.onHandleAgeChange.bind(this);
  }

  onHandleNameChange(e) {
    this.setState({
      name: e.target.value,
    });
  }

  onHandleAgeChange(e) {
    this.setState({
      age: e.target.value,
    });
  }

  render() {
    const { name, age } = this.state;
    return (
      
        
Name
Age
); } } export function Greeting() { const [name, setName] = useState('Albus Dumbledore'); const [age, setAge] = useState(175); function handleNameChange(e) { setName(e.target.value); } function handleAgeChange(e) { setAge(e.target.value); } return (
Name
Age
); }

Basic concept of hooks

  • useState
  • useEffect
  • customHooks
  • otherHooks

useState

function ExampleWithManyStates() {
  // Declare multiple state variables!
  const [age, setAge] = useState(42);
  const [fruit, setFruit] = useState('banana');
  const [todos, setTodos] = useState([{ text: 'Learn Hooks' }]);
  // ...
}

useEffect

  • side effects - can affect other components and can’t be done during rendering
  • React runs the effects after every render — including the first render
  • same purpose as cDM, cDU, cWU
  • data fetching, manually event listener, manually changing the DOM etc…

useEffect

cDM & cDU

import { useState, useEffect } from 'react';

function Example() {
  const [count, setCount] = useState(0);

  // Similar to componentDidMount and componentDidUpdate:
  useEffect(() => {
    // Update the document title using the browser API
    document.title = `You clicked ${count} times`;
  });

  return (
    
); }

useEffect

cWU

import { useState, useEffect } from 'react';

function FriendStatus(props) {
    const [isOnline, setIsOnline] = useState(null);

    function handleStatusChange(status) {
        setIsOnline(status.isOnline);
    }

    useEffect(() => {
        ChatAPI.subscribeToFriendStatus(props.friend.id, handleStatusChange);

        return () => {
            ChatAPI.unsubscribeFromFriendStatus(props.friend.id, handleStatusChange);
        };
    });

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

Custom Hooks

  • replace igher-order components and render props for reuse statefull logic
  • naming convention is `useSomething`
  • use custom hooks for handling, animation, declarative subscriptions, timers…

Custom Hooks

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

export function useWindowWidth() {
  const [width, setWidth] = useState(window.innerWidth);
  useEffect(() => {
    const handleResize = () => setWidth(window.innerWidth);
    window.addEventListener('resize', handleResize);
    return () => {
      window.removeEventListener('resize', handleResize);
    };
  });
  return width;
}

export function useDocumentTitle(title) {
  useEffect(() => {
    document.title = title;
  });
}

export function useFormInput(initialValue) {
  const [value, setValue] = useState(initialValue);
  function onHandleChange(e) {
    setValue(e.target.value);
  }
  return { value, onChange: onHandleChange };
}

export function CustomHook() {
  const title = useFormInput('Heading');
  const designation = useFormInput('Software Engineer');
  const width = useWindowWidth();
  useDocumentTitle(title.value);
  return (
    
      
Title
Designation
Width
); }

useContext

  • new Context API released in react 16.3
function Example() {
    const locale = useContext(LocaleContext);
    const theme = useContext(ThemeContext);
    // ...
}

useReducer

Use hooks like redux

import React, { useReducer } from 'react';

const initialState = {
  loading: false,
  count: 0,
};

const reducer = (state, action) => {
  switch (action.type) {
    case 'increment': {
      return { ...state, count: state.count + 1, loading: false };
    }
    case 'decrement': {
      return { ...state, count: state.count - 1, loading: false };
    }
    case 'loading': {
      return { ...state, loading: true };
    }
    default: {
      return state;
    }
  }
};

const delay = (time = 1500) => {
  return new Promise(resolve => {
    setTimeout(() => {
      resolve(true);
    }, time);
  });
};

function PokemonInfo() {
  const [{ count, loading }, dispatch] = useReducer(reducer, initialState);
  const onHandleIncrement = async () => {
    dispatch({ type: 'loading' });
    await delay(500);
    dispatch({ type: 'increment' });
  };
  const onHandleDecrement = async () => {
    dispatch({ type: 'loading' });
    await delay(500);
    dispatch({ type: 'decrement' });
  };
  return (
    

Count {loading ? 'loading..' : count}

); } export default PokemonInfo;

other hooks

  • useRef
  • useMemo
  • useCallback

How react hooks works under the hood?

when react will call useEffect?

By using this Hook, you tell React that your component needs to do something after render. React will remember the function you passed (we’ll refer to it as our “effect”), and call it later after performing the DOM updates.

Does useEffect run after every render?

  • Yes! By default, it runs both after the first render and after every update.
  • but you can pass second argument to skip effect

effect will run after every render

function FriendStatus(props) {
    // ...
    useEffect(() => {
        ChatAPI.subscribeToFriendStatus(props.friend.id, handleStatusChange);
        return () => {
            ChatAPI.unsubscribeFromFriendStatus(props.friend.id, handleStatusChange);
        };
    });
}


// Mount with { friend: { id: 100 } } props
ChatAPI.subscribeToFriendStatus(100, handleStatusChange);     // Run first effect

// Update with { friend: { id: 200 } } props
ChatAPI.unsubscribeFromFriendStatus(100, handleStatusChange); // Clean up previous effect
ChatAPI.subscribeToFriendStatus(200, handleStatusChange);     // Run next effect

// Update with { friend: { id: 300 } } props
ChatAPI.unsubscribeFromFriendStatus(200, handleStatusChange); // Clean up previous effect
ChatAPI.subscribeToFriendStatus(300, handleStatusChange);     // Run next effect

// Unmount
ChatAPI.unsubscribeFromFriendStatus(300, handleStatusChange); // Clean up last effect

skip effect by pass second argument

// old way
componentDidUpdate(prevProps, prevState) {
    if (prevState.count !== this.state.count) {
        document.title = `You clicked ${this.state.count} times`;
    }
}

// hooks way
useEffect(() => {
    document.title = `You clicked ${count} times`;
}, [count]); // Only re-run the effect if count changes

what if I pass [] as second argument

function Counter() {
  const [count, setCount] = useState(0);

  useEffect(() => {
    const id = setInterval(() => {
      setCount(count + 1);
    }, 1000);
    return () => clearInterval(id);
  }, []);

  return 

{count}

; }

what if I used one props/state but not passed as second argument

useEffect(() => {
  document.title = 'Hello, ' + name;
}, []); // Wrong: name is missing in deps

Rules of Hooks and Why

Hooks and javascript closures

Demo

function Counter() {
  const [count, setCount] = useState(0);

  function handleAlertClick() {
    setTimeout(() => {
      alert('You clicked on: ' + count);
    }, 3000);
  }
  return (
    

You clicked {count} times

); }

Use Hooks to replace componentDidMount?

the ability of the hook useCallback

Is hooks ready for today?

  • technically, yes, is in v16.7.0-alpha
  • but you better to try this on non-critical component first

Q&A

Thanks