React Hooks Getting Started Tutorial

React Hooks Getting Started Tutorial

React is the mainstream front-end framework. The v16.8 version introduces a new API called React Hooks , which subverts the previous usage.

This API is the future of React and it is necessary to understand it deeply. This article talks about my understanding, briefly introduces its usage, and helps you get started quickly.

Reading this article requires a basic React foundation. If you haven’t learned React yet, take a look at its tutorial first .

Disadvantages of component classes

At the heart of React are components. Before v16.8, the standard way of writing components was class. Below is a simple component class.


import React, { Component } from "react";

export default class Button extends Component {
  constructor() {
    super();
    this.state = { buttonText: "Click me, please" };
    this.handleClick = this.handleClick.bind(this);
  }
  handleClick() {
    this.setState(() ={
      return { buttonText: "Thanks, been clicked!" };
    });
  }
  render() {
    const { buttonText } = this.state;
    return <button onClick={this.handleClick}>{buttonText}</button>;
  }
}


(click to view the running result )

This component class is just a button, but as you can see, its code is already “heavy”. The real React App is composed of multiple classes hierarchically, layer by layer, and the complexity increases exponentially. Add Redux and it gets more complicated.

Dan Abramov, author of Redux, summarizes several disadvantages of component classes.

  • Large components are hard to split and refactor, and hard to test.
  • Business logic is scattered among the various methods of the component, resulting in duplicate logic or associated logic.
  • Component classes introduce complex programming patterns such as render props and higher-order components.

functional component

The React team hopes that components don’t become complex containers, preferably just pipes for data flow. Developers can combine pipelines as needed. Components are best written as functions, not classes.

React has long supported functional components , and here is an example.


function Welcome(props) {
  return <h1>Hello, {props.name}</h1>;
}


However, this way of writing has significant limitations, it must be a pure function, cannot contain state, and does not support lifecycle methods, so it cannot replace a class.

The design purpose of React Hooks is to enhance the functional components, and you can write a full-featured component without using “classes” at all.

Meaning of Hook

React Hooks means that components should be written as pure functions as much as possible. If external functions and side effects are required, use hooks to “hook” external code in. React Hooks are those hooks.

Use whatever hooks you need. React provides some common hooks by default, and you can also encapsulate your own hooks.

All hooks introduce external functions to functions, so according to the React convention, hooks are always usenamed with a prefix for easy identification. If you want to use the xxx function, the hook is named usexxx.

The following describes the four most commonly used hooks provided by React by default.

  • useState()
  • useContext()
  • useReducer()
  • useEffect()

useState(): state hook

useState()Used to introduce state for functional components. Pure functions cannot have state, so put the state inside the hook.

The component class earlier in this article, the user clicks the button, will cause the text of the button to change, the text depends on whether the user clicks, this is the state. Use useState()rewrite as follows.


import React, { useState } from "react";

export default function  Button()  {
  const  [buttonText, setButtonText] =  useState("Click me,   please");

  function handleClick()  {
    return setButtonText("Thanks, been clicked!");
  }

  return  <button  onClick={handleClick}>{buttonText}</button>;
}


(click to view the running result )

In the above code, the Button component is a function that uses useState()hooks to introduce state internally.

useState()This function accepts the initial value of the state as a parameter, the initial value of the above example is the text of the button. The function returns an array whose first member is a variable (as in the example above buttonText) that points to the current value of the state. The second member is a function to update the state, the convention is to setprefix the variable name of the state (as in the above example setButtonText).

useContext(): shared state hook

Can be used if you need to share state between components useContext().

There are now two components Navbar and Messages, and we want to share state between them.

<div className="App">
  <Navbar/>
  <Messages/>
</div>

The first step is to use the React Context API to create a Context outside the component.

const AppContext = React.createContext({});

The component package code is as follows.


<AppContext.Provider value={{
  username: 'superawesome'
}}>
  <div className="App">
    <Navbar/>
    <Messages/>
  </div>
</AppContext.Provider>


In the above code, AppContext.Providera Context object is provided, which can be shared by child components.

The code for the Navbar component is as follows.


const Navbar = () ={
  const { username } = useContext(AppContext);
  return (
    <div className="navbar">
      <p>AwesomeSite</p>
      <p>{username}</p>
    </div>
  );
}


In the above code, the useContext()hook function is used to introduce the Context object and obtain usernameproperties from it.

The code for the Message component is similar.


const Messages = () ={
  const { username } = useContext(AppContext)

  return (
    <div className="messages">
      <h1>Messages</h1>
      <p>1 message for {username}</p>
      <p className="message">useContext is awesome!</p>
    </div>
  )
}


(click to view the running result )

useReducer(): action hook

React itself does not provide state management capabilities and usually requires the use of external libraries. The most commonly used library for this is Redux.

The core concept of Redux is that components emit actions to communicate with the state manager. After the state manager receives the action, it uses the Reducer function to calculate the new state. The Reducer function is in the form of (state, action) =newState.

useReducers()Hooks are used to introduce Reducer functionality.


const [state, dispatch] = useReducer(reducer, initialState);


The above is useReducer()the basic usage, it accepts the Reducer function and the initial value of the state as parameters and returns an array. The first member of the array is the current value of the state, and the second member is the dispatchfunction that sent the action.

Below is an example of a counter. The Reducer function used to compute the state is as follows.


const myReducer = (state, action) ={
  switch(action.type)  {
    case('countUp'):
      return  {
        ...state,
        count: state.count + 1
      }
    default:
      return  state;
  }
}


The component code is as follows.


function App() {
  const [state, dispatch] = useReducer(myReducer, { count:   0 });
  return  (
    <div className="App">
      <button onClick={() =dispatch({ type: 'countUp' })}>
        +1
      </button>
      <p>Count: {state.count}</p>
    </div>
  );
}


(click to view the running result )

Since Hooks can provide shared state and Reducer functions, it can replace Redux in these areas. However, it can’t provide middleware and time travel, if you need these two features, you still use Redux.

useEffect(): side effect hook

useEffect()Used to introduce operations with side effects, most commonly requesting data from the server. componentDidMountThe code that used to be placed inside can now be placed inside useEffect().

useEffect()The usage is as follows.


useEffect(()  = {
  // Async Action
}, [dependencies])


In the above usage, useEffect()two parameters are accepted. The first parameter is a function in which the code for the asynchronous operation is placed. useEffect()The second parameter is an array that gives Effect’s dependencies, which will be executed whenever this array changes . The second parameter can be omitted, and it will be executed every time the component is rendered useEffect().

See an example below.


const Person = ({ personId }) ={
  const [loading, setLoading] = useState(true);
  const [person, setPerson] = useState({});

  useEffect(() ={
    setLoading(true); 
    fetch(`https://swapi.co/api/people/${personId}/`)
      .then(response =response.json())
      .then(data ={
        setPerson(data);
        setLoading(false);
      });
  }, [personId])

  if (loading === true) {
    return <p>Loading ...</p>
  }

  return <div>
    <p>You're viewing: {person.name}</p>
    <p>Height: {person.height}</p>
    <p>Mass: {person.mass}</p>
  </div>
}


In the above code, whenever the component parameters personIdchange, useEffect()it will be executed. useEffect()It is also executed when the component is rendered for the first time .

(click to view the running result )

Create your own Hooks

The Hooks code in the above example can also be encapsulated into a custom Hook for easy sharing.


const usePerson = (personId) ={
  const [loading, setLoading] = useState(true);
  const [person, setPerson] = useState({});
  useEffect(() ={
    setLoading(true);
    fetch(`https://swapi.co/api/people/${personId}/`)
      .then(response =response.json())
      .then(data ={
        setPerson(data);
        setLoading(false);
      });
  }, [personId]);  
  return [loading, person];
};


In the above code, it usePerson()is a custom Hook.

The Person component uses this new hook instead, introducing encapsulation logic.


const Person = ({ personId }) ={
  const [loading, person] = usePerson(personId);

  if (loading === true) {
    return <p>Loading ...</p>;
  }

  return (
    <div>
      <p>You're viewing: {person.name}</p>
      <p>Height: {person.height}</p>
      <p>Mass: {person.mass}</p>
    </div>
  );
};


(click to view the running result )

Reference link