React Hooks

Why are they necessary?

- Don't need Classes to use all React features.

- Provide a more direct API to the common concepts (props, state, context, refs and lifecycle).

- Reuse stateful logic to avoid Huge components.

- Don't rewrite your components (Use them from new components by using smaller functions instead).

- No breaking changes (100% backwards compatible), more details here.

What’s a Hook?

A Hook is a special function that lets you “hook into” React features.

useState: is a Hook that lets you add React state to function components

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

Before

import React, { useState } from 'react';

function Example() {
  // Declare a new state variable
  const [count, setCount] = useState(0);

After

Returns a stateful value and a function to update it.

const App = class App extends Component {
  state = {
    appState: AppState.currentState
  }
  async componentDidMount () {
    AppState.addEventListener('change', this._handleAppStateChange.bind(this))
  }

  componentWillUnmount () {
    AppState.removeEventListener('change', this._handleAppStateChange.bind(this))
  }

Example: Detect AppState changes

function useAppState() { 
  const [appState, setAppState] = useState(AppState.currentState)
  const onChange = (newState) => setAppState(newState)
  useEffect(() => {
    AppState.addEventListener('change', onChange)
    return () => {
      AppState.removeEventListener('change', onChange)
    }
  }, [])
  return appState
}

Or using a smaller function that can be reused from any component

useEffect

// Without the second parameter
useEffect(() => {
  console.log('I will run after every render');
});

// With the second parameter
useEffect(() => {
  console.log('I will run only when valueA changes');
}, [valueA]);

Like "componentDidMount":

// With zero dependencies
useEffect(() => {
  console.log('I will run only once');
}, []);

Perform side effects from a function component.

Similar to lifecycle methods in classes:

Like "componentDidUpdate":

// Ignore the first render
const didMountRef = useRef(false);
useEffect(() => {
  if (didMountRef.current) {
    console.log('I will run when an update occurs!');
  } else didMountRef.current = true;
});

When a prop changes:

const MyFunComponent = (props) => {
  useEffect(() => {
    console.log('I will run when a prop changes!');
  }, Object.keys(props).sort().map(k => props[k]));
}

Rules of Hooks

  • Only call Hooks at the top level. Don’t call Hooks inside loops, conditions, or nested functions. Example
  • Only call Hooks from React function components or custom hooks.

Reuse stateful logic

The idea with this is collocate the side effects, the changes of the state in a reusable component.

// State variables can hold objects and arrays
const [todos, setTodos] = useState([{ id: 123, text: 'Learn Hooks' }]);

But unlike this.setState in a class, updating a state variable always replaces it instead of merging it...

// Remove an item by id
const deletedId = 123;
setTodos(tasks => tasks.filter(task => task.id !== deletedId))

An option to fix that would be:

useReducer to the Rescue!

The correct place to update the state and ¿notify changes?

const initialState = {
    data: [],
    error: null,
    loading: true,
    ...
}

const [state, dispatch] = useReducer((state, action) => {
	switch (action.type) {
      case 'publish':
      	// This is a perfect place to save the state or call external APIs
        applyChanges(state)
      	break;
      case 'newMessage':
        const newData = unionBy([action.value], [...state.data], 'id')
        return { ...state, data: newData }
      default:
        return { ...state, ...action }
    }
}, initialState)

More hooks to learn here!

React Hooks

By J.D Nicholls

React Hooks

About React Hooks

  • 3,318