React Hooks

Amr Draz

Problem

How to re-use and share component logic?

Higher order components

Render Props

Wrapper Hell

Wrapper Hell

screenshot from react conf talk on hooks

Life cycle logic

Create a class just to add some state

export function Counter({ count, setCount }) {
  return <div>
     <span>count</span>
     <button onClick={() => setCount(count + 1)}>+</button>
     <button onClick={() => setCount(count - 1)}>+</button>
  </div>
}

export class StatfulCounter extends Component  {

  constructor(props) {
    super(props)
    this.state = {
       count: 0
    }
    this.setCount = (count) => {
      this.setState({ count })
    } 
  }

  render() {
    const count = this.state
    const setCount = this
    return <div>
     <span>count</span>
     <button onClick={() => setCount(count + 1)}>+</button>
     <button onClick={() => setCount(count - 1)}>+</button>
   </div>
  }
}

Classes are hard for humans but also hard for machines to optamise

Proposal Hooks

Experimental

You can try it now in react 16.7-alpha

import React, { Component }

export class StatfulCounter extends Component {

  constructor(props) {
    super(props)
    this.state = {
       count: 0
    }
    this.setCount = (count) => {
      this.setState({ count })
    } 
  }

  render() {
    const count = this.state
    const setCount = this
    return <div>
     <span>count</span>
     <button onClick={() => setCount(count + 1)}>+</button>
     <button onClick={() => setCount(count - 1)}>+</button>
   </div>
  }
}
import React, { useState } from "react"
 
export function StatfulCounter () {

  const [count, setCount] = useState(0)

  return <div>
     <span>count</span>
     <button onClick={() => setCount(count + 1)}>+</button>
     <button onClick={() => setCount(count - 1)}>+</button>
  </div>
}

useHooks

import React, { useState } from "react"
 
export function StatfulCounter () {

  const [state, setState] = useState({ count: 0 })

  return <div>
     <span>state.count</span>
     <button onClick={() => setState({ count: state.count + 1 })}>+</button>
     <button onClick={() => setState({ count: state.count - 1 })}>+</button>
  </div>
}

useState


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

useState

From docs

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

class FriendStatus extends Component {
  state = { isOnline: null, count: null }

  handleStatusChange = (status) => {
    this.setState({ isOnline: status.isOnline });
  }

  setCounter = (count) => this.setState({ count })

  componentDidMount() {
    ChatAPI.subscribeToFriendStatus(props.friend.id, handleStatusChange);
    ChatAPI.getMessageCount().then(setCounter)
  }

  componentWillUnmount() {
    ChatAPI.unsubscribeFromFriendStatus(props.friend.id, handleStatusChange);
  }
  
  render() {
      const { isOnline, count } = this

      if (isOnline === null && count === null ) {
        return 'Loading...';
      }

      return <div>
        <span>{count}</span><span>isOnline ? 'Online' : 'Offline'</span>;
      </div>
  }
}

LifeCycle

import { useState, useEffect } from 'react';

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

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

  useEffect(() => {
    ChatAPI.getMessageCount().then(setCounter)
  })

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

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

  if (isOnline === null && count === null ) {
    return 'Loading...';
  }

  return <div>
    <span>{count}</span><span>isOnline ? 'Online' : 'Offline'</span>;
  </div>
}

useEffect

import { useState, useEffect } from 'react';

function FriendStatus(props) {

  count count = useCounterLogic()
  count isOnline = useFriendLogic()

  if (isOnline === null && count === null ) {
    return 'Loading...';
  }

  return <div>
    <span>{count}</span><span>isOnline ? 'Online' : 'Offline'</span>;
  </div>
}

function useCounterLogic() {
  const [count, setCount] = useState(null);
  useEffect(() => {
    ChatAPI.getMessageCount().then(setCounter)
  })
  return count
}

function useFriendLogic() {
  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);
    };
  });

  return isOnline
}

useCustom

How Does it work?

React Hooks

By Amr Draz

React Hooks

  • 95