React Hooks

Introducing Hooks

Hooks are a new addition in React 16.8. They let you use some  React features that before were not possible without writing a class. Some of these features are: state, life cycle methods (componentDidMount, componentWillReceiveProps, etc), context, pure component, and refs.
Also, let you connect to a fresh new API where you can write your own hooks functions that let you “hook into” React state and lifecycle features from function components.

 

No need for classes...

Why Using a Hook ?

  • Reduce code implementation: Pass from having a class based to only have a functional based component. This also means a reduction of bytes from bundle size.

  • Logic is now shareable: Writing custom hooks we can now share logic between components easily than before.

  • Easier life cycle methods: React life cycle methods are easier to reason especially if you are new to React, fewer methods to memorize.

  • Performance gains: The gains are minimal but still React perform better with hooks than with class components.

  • Classes confuse both people and machines: You have to understand how this works in JavaScript, which is very different from how it works in most languages. You have to remember to bind the event handlers etc.
    Also, classes don’t minify very well, and they make hot reloading flaky and unreliable.

Adding Hooks to React

At React Conf 2018, Sophie Alpert and Dan Abramov introduced Hooks, followed by Ryan Florence demonstrating how to refactor an application to use them

However, there are some catches

  • ⚠️ You should call hooks at the top level of the render function.
    These means don’t call Hooks inside loops, conditions, or nested functions. Instead, always use Hooks at the top level of your React function. By following this rule, you ensure that Hooks are called in the same order each time a component renders.

  • ⚠️ You can only use hooks in React Function Components, and in Custom Hooks

 

        It means that you Don’t call Hooks from regular JavaScript functions.​

  • ⚠️ There aren’t hook primitives for componentDidCatch or getSnapshotBeforeUpdate
    The React team says they are on their way. For the componentDidCatch use case, you could create an Error Boundary component.

  • ✅ ESLint Plugin
    There is an ESLint plugin called eslint-plugin-react-hooks that enforces these rules.

 
 

Class features vs. Hooks equivalents: State

import React, { useState } from 'react'

function Example() {
  // Declare a new state, we can have more than one
  const [count, setCount] = useState(0) // Initial state
  const [user, setUser] = useState({ name: 'Joni' lastName: 'del Valle' })

  return (
    <div>
      <p>You clicked {count} times</p>
      <button onClick={() => setCount(count + 1)}>
        Change Count
      </button>
      <p>User: {user.name} {user.lastName}</p>
      <button onClick={() => setUser({...user, name: 'Ariel' })}>
        Change User
      </button>
    </div>
  );
}

export default Example

State

//Class
import React, { Component } from 'react'

class CounterButton extends Component {
    constructor() {
        super()
        this.state = {
            count: 0
        }
    }
    render() {
        return <button onClick={() => this.setState({ count: this.state.count + 1 })}>
            { this.state.count }
         </button>
    }
}

export default CounterButton

//Hooks
import React, { useState } from 'react'

const CounterButton = props => {
    const [count, setCount] = useState(0);

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

export default CounterButton

ComponentDidUpdate

//Class
import React, { Component } from 'react'

class ClassComponent extends Component {
    componentDidUpdate() {
         console.log('Just updated...')
    }

    render() {
        return <div>Class Component</div>
    }
}

export default ClassComponent

//Hooks
import React, { useEffect } from 'react'

const FunctionalComponent = props => {
    useEffect(() => {
        console.log('Just updated...')
    })

    return <div>Functional Component </div>
}

export default FunctionalComponent

ComponentDidMount

//Class
import React, { Component } from 'react'

class ClassComponent extends Component {
    componentDidMount() {
         console.log('I just mounted!')
    }

    render() {
        return <div>Class Component</div>
    }
}

export default ClassComponent

//Hooks
import React, { useEffect } from 'react'

const FunctionalComponent = props => {
    useEffect(() => {
        console.log('I just mounted!')
    }, [])

    return <div>Functional Component </div>
}

export default FunctionalComponent

ComponentWillUnmount

//Class
import React, { Component } from 'react'

class ClassComponent extends Component {
    componentWillUnmount() {
         console.log('I am unmounting!')
    }

    render() {
        return <div>Class Component</div>
    }
}

export default ClassComponent

//Hooks
import React, { useEffect } from 'react'

const FunctionalComponent = props => {
    useEffect(() => {
        return () => console.log('I am unmounting')
    }, [])

    return <div>Functional Component </div>
}

export default FunctionalComponent

ComponentWillReceiveProps

//Class
import React, { Component } from 'react'

class ClassComponent extends Component {
    componentWillReceiveProps(nextProps) {
        if (nextProps.count !== this.props.count) {
            console.log('count changed', nextProps.count)
        }
    }

    render() {
        return <div>Class Component</div>
    }
}

export default ClassComponent

//Hooks
import React, { useEffect } from 'react'

const FunctionalComponent = props => {
    useEffect(() => {
        console.log('count changed', props.count);
    }, [props.count])

    return <div>Functional Component </div>
}

export default FunctionalComponent

DOM Refs 

import React, { Component } from 'react' //Class

class ClassComponent extends Component {
    constructor() {
        super()
        this.inputRef = null
    }
    render() {
        return <>
            <input ref={inputRef => { this.inputRef = inputRef }} />
            <button onClick={() => this.inputRef.focus()}>
                Focus the input
            </button>
        </>
    }
}

export default ClassComponent

import React, { useRef } from 'react' //Hooks

const FunctionalComponent = props => {
    const inputRef = useRef()
    return <>
            <input ref={inputRef} />
            <button onClick={() => inputRef.current.focus()}>
                Focus the input
            </button>
        </>
}

export default FunctionalComponent

ShouldComponentUpdate 

import React, { Component } from 'react' //Class

class ClassComponent extends Component {
    shouldComponentUpdate(nextProps) {
        return nextProps.count !== this.props.count
    }
    render() {
        return <div>Class Component</div>
    }
}

export default ClassComponent

import React, { memo } from 'react' //Hooks

const FunctionalComponent = props => {
    return <div>Functional Component</div>
}

const MyFunctionalComponent = memo(
    FunctionalComponent, 
    (prevProps, nextProps) => nextProps.count !== prevProps.count
)

export default MyFunctionalComponent

We gonna use memo for this one, while this is not a Hook, it’s still part of the class-to-functional-component migration plan

this.myVar 


const Timer = (props) => {
    const intervalRef = useRef();

    useEffect(() => {
        const id = setInterval(() => {
            // ...
        });
        intervalRef.current = id;
        return () => {
            clearInterval(intervalRef.current);
        };
    });
}

useRef has another cool usage besides DOM refs, it is also a generic container whose current property is mutable and can hold any value, similar to an instance property on a class

Previous state\props

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

    const prevCountRef = useRef();
    useEffect(() => {
        prevCountRef.current = count;
    });
    const prevCount = prevCountRef.current;

    return <h1>Now: {count}, before: {prevCount}</h1>;
}

Some lifecycle method, like componentDidUpdate, provide the previous state and props. If you really need the previous values for your Hooks, this can be imitated the following way (using yet again our good friend – useRef)

More...

Thank you

Next Step...