React v16.8

React v16.8

The One With Hooks

Road Map

  • What are Hooks?

  • Why?

  • Initial Concerns

  • Examples

What are Hooks?

  • Class Features in a Function

  • State in a Function

  • Reusable Logic

  • New Direction of React

Why?

  • What's wrong with classes?

  • Code co-location

  • One standard for components

Hopefully: Simplicity

Initial Concerns

  • Backwards compatible

  • Only affects functional components

  • No Reason to change old code

Examples

  • useState

  • useEffect

  • useReducer

useState()

Class Component

Functional Component

class Example extends React.Component {
  constructor(props) {
    super(props);
    this.state = {
      count: 0
    };
  }

  handleClick = () => {
    this.setState({ count: this.state.count + 1 });
  };

  render() {
    return (
      <div>
        <p>You clicked {this.state.count} times</p>
        <button onClick={this.handleClick}>
            Click me
        </button>
      </div>
    );
  }
}
const Example = () => {
  const [count, setCount] = useState(0);

  const handleClick = () => {
    setCount(count + 1);
  };

  return (
    <div>
      <p>You clicked {count} times</p>
      <button onClick={handleClick}>
        Click me
      </button>
    </div>
  );
};

useState()

Multiple State Variables

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

Note: State can still be an object, but updating is not a merge, it is a replacement.

useEffect()

useEffect(() => {

    // Do something when I render

    return () => { 
        // do this when I unrender/unmount
    }

}, [ // only run when this list has changed ]);

useEffect()

Class Component

Functional Component

class Example extends React.Component {
  constructor(props) {
    ...
  }

  componentDidMount() {
    document.title = `You clicked ${this.state.count} times`;
  }

  componentDidUpdate() {
    document.title = `You clicked ${this.state.count} times`;
  }

  handleClick = () => {
    this.setState({ count: this.state.count + 1 });
  }

  render() {
    ...
  }
}
const Example = () => {
  const [count, setCount] = useState(0);

  useEffect(() => {
    document.title = `You clicked ${count} times`;
  });

  const handleClick = () => {
    setCount(count + 1);
  }

  return (
    ...
  );
};

cDM and cDU

useEffect()

Class Component

Functional Component

class Example extends React.Component {
  constructor(props) {
    ...
  }

  componentDidMount() {
    document.title = `You clicked ${this.state.count} times`;
  }

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

  handleClick = () => {
    this.setState({ count: this.state.count + 1 });
  }

  render() {
    ...
  }
}
const Example = () => {
  const [count, setCount] = useState(0);

  useEffect(() => {
    document.title = `You clicked ${count} times`;
  }, [count]);

  const handleClick = () => {
    setCount(count + 1);
  }

  return (
    ...
  );
};

Conditional cDU

useEffect()

Class Component

Functional Component

class Example extends React.Component {
  constructor(props) {
    ...
  }

  componentDidMount() {
    const savedClicks = fetch(clicks);
    this.setState({
      count: savedClicks
    });
  }

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

  handleClick = () => {
    this.setState({ count: this.state.count + 1 });
  }

  render() {
    ...
  }
}
const Example = () => {
  const [count, setCount] = useState(0);

  useEffect(() => {
    const savedClicks = fetch(clicks);
    setCount(savedClicks);
  }, []);

  useEffect(() => {
    document.title = `You clicked ${count} times`;
  }, [count]);

  const handleClick = () => {
    setCount(count + 1);
  }

  return (
    ...
  );
};

Data Fetching

useReducer()

const initialState = {count: 0};

function reducer(state, action) {
  switch (action.type) {
    case 'increment':
      return {count: state.count + 1};
    case 'decrement':
      return {count: state.count - 1};
    default:
      throw new Error();
  }
}

function Counter({initialState}) {
  const [state, dispatch] = useReducer(reducer, initialState);
  return (
    <>
      Count: {state.count}
      <button onClick={() => dispatch({type: 'increment'})}>+</button>
      <button onClick={() => dispatch({type: 'decrement'})}>-</button>
    </>
  );
}

Additional Reading

  • useReducer()

  • useContext()

  • Custom Hooks

  • reactjs.org - Great docs covering Hooks

  • overreacted.io - Dan Abramov's Blog

  • React Today and Tomorrow - Video introducing Hooks

Additional Topics

React v16.8

By Michael Klosterboer