The Guide to Learning React Hooks by Eric Bishard

Chapter 1: On React Hooks

It was released in beta 16.7.0-alpha.0 in October 2018, a month after Facebook had used it in production, ensuring that the community didn’t face major bugs and issues. Because large refactorings that break backward compatibility tend to be problematic, React employs a gradual migration and adoption strategy that allows new apis and patterns to coexist with older ones. Hooks are additions to the core library. This means that it is optional and backward compatible. They posted a request for comment on the process ahead of GITHUB. If you want to use them, just install the latest version of React. This Hooks pattern provides an alternative to writing class components that simply use state and lifecycle methods. Hooks enable function components to use things that only class components can use, such as React Local State, Effects, and Context via useState, useEffect, and useContext. Other Hooks include useReducer, useCallback, useMemo, useRef, useImperativeHandle, useLayoutEffect, useDebugValue.

So how do you use Hooks

The most effective way to show this is to use a comparative example, a way to write using class components that require access to state and lifecycle methods; Another way to use function components to do the same thing. Below I provide a working example similar to the one in the ReactJS documentation, but you can modify and test it, using the StackBlitz demo to modify the tests yourself at each stage of our learning. So let’s stop talking about it and start learning React Hooks.

The advantages of using Hooks

Hooks are also great for developers because they change the way we write components. Can help us write clearer, more concise code — like when we go on a code diet, we lose a lot of weight, look better, feel better. It makes our jawline protrude and makes our toes feel lighter. It looks like this.


Five important rules of Hooks

Before we create our own Hooks, let’s review some of the main rules that we must always follow.

  1. Do not call Hooks from inside loops, conditions, or nested functions
  2. Use hooks only at the top level
  3. Only the React function calls the Hook
  4. Never call a Hook in a normal JavaScript function
  5. The ES Lint plugin can be used to enforce these rules in teams if needed. There is also a good explanation of why these rules are needed on the same page.

Hooks for State and Effects

The difference between a class component and a function component shown in this GIF will be explained in more detail later.

Use useState to show the difference between a class and a function counting component

Let’s first look at the example counting component shown in the React document. It’s a simple component that includes a button that pushes the state forward and updates state.count for rendering when clicked. First, let’s look at the class component, using setState to update the state.

import React from 'react';

class Counter extends React.Component {
  constructor() {
    this.state = { count: 0 };
    this.incrementCount = this.incrementCount.bind(this);
  }

  incrementCount() {
    this.setState({ count: this.state.count + 1 });
  }

  render() {
    return (
      <div>
        <p>You clicked {this.state.count} times</p> <button onClick={this.incrementCount}>Click Me</button> </div> ); }}export default Counter;
Copy the code

The first thing to notice is that we need to use the class syntax, declaring a constructor in which we can reference the this keyword. There is a state property in the constructor, and the state is updated using the setState() method. Let’s look at how this function component is implemented using Hooks.

import React, { useState } from 'react';

const Counter = (a)= > {
  const [count, setCount] = useState(0);
  const incrementCount = (a)= > setCount(count + 1);

  return (
    <div>
      <p>You clicked {count} times</p>
      <button onClick={incrementCount}>Click Me</button>
    </div>)}export default Counter;
Copy the code

In this function component, we introduce a useState attribute, with no other class syntax or constructor. Its assignment sets the default value and provides not only the count attribute, but also a function called setCount that modifies the state. This setCount is a function method, you can call it whatever you want. The component method incrementCount is more readable and can refer to our state value between references instead of this.state.

Comparison of useEffect methods

Sometimes side effects occur when updating the status. In our counting component, we might need to update the database, change the local store, or change the title of the document. In the React JS documentation, the latter example is used to keep things as simple as possible. So let’s update our example and use the new hook USEffect to have a side effect. Let’s add this side effect to our existing example, and then look at methods that use classes and hooks. Let’s first look at the implementation using class components.

import React from 'react';

class Counter extends React.Component {
  constructor() {
    this.state = { count: 0 };
    this.incrementCount = this.incrementCount.bind(this);
  }
  incrementCount() {
    this.setState({ count: this.state.count + 1 });
  }
  
  componentDidMount() { document.title = `You clicked ${this.state.count} times`; }
  componentDidUpdate() { document.title = `You clicked ${this.state.count} times`; }

  render() {
    return (
      <div>
        <p>You clicked {this.state.count} times</p> <button onClick={this.incrementCount}>Click Me</button> </div> ); }}export default Counter;
Copy the code

Then, implement the same method using Hooks.

import React, { Component, useState, useEffect } from 'react';
function Counter() {
  const [count, setCount] = useState(0);
  const incrementCount = () => setCount(count + 1);

  useEffect(() => {
    document.title = `You clicked ${count} times`});return (
    <div>
      <p>You clicked {count} times</p>
      <button onClick={incrementCount}>Click me</button>
    </div>
  )
}

export default Counter;
Copy the code

Now that we’ve introduced additional behavior, we’re starting to see more evidence of how switching to hooks provides a cleaner way to handle states and side effects. In a function component, you can use a useEffect method to do what two methods do in a class component. Simply removing the class syntax and an extra method makes our code more readable. Totally worth it.

You can call useState and useEffect as many times as you want

Just like with setState, you can call useState multiple times. Let’s switch to an example that shows a slightly more complicated situation where we display a name on the page, some input that allows you to change the name, and we want to control both the first and last name. We need to create two properties, each with its own update and set methods. Just set the default values for each call to useState. You can see what it looks like in the GIF below, and how it looks in the class-based version, which we’ll explore further below.

import React, { Component } from 'react';

export default class Greeting extends React.Component {
  constructor(props) {
    super(props);
    this.state = {
      firstName: 'Harry',
      lastName: 'Poppins'
    };
    this.handleFirstNameChange = this.handleFirstNameChange.bind(this);
    this.handleLastNameChange = this.handleLastNameChange.bind(this);
  }

  handleFirstNameChange = (e) => this.setState({ firstName: e.target.value });
  handleLastNameChange = (e) => this.setState({ lastName: e.target.value });

  render() {
    return( <div> <input value={this.state.firstName} onChange={this.handleFirstNameChange}/><br /> <input value={this.state.lastName} onChange={this.handleLastNameChange}/> <p> <span>{this.state.firstName} {this.state.lastName}</span> </p> </div> ); }}Copy the code

The use of Hooks:

import React, { Component, useState } from 'react';
export default function Greeting() {
  
  const [firstName, setFirstName] = useState("Bat");
  const [lastName, setLastName] = useState("Man");;

  const handleFirstNameChange = (e) => setFirstName(e.target.value);
  const handleLastNameChange = (e) => setLastName(e.target.value);

  return (
    <div>
      <input value={firstName} onChange={handleFirstNameChange} /><br />
      <input value={lastName} onChange={handleLastNameChange} />
      <p>
        Hello, <span>{firstName} {lastName}</span>
      </p>
    </div>
  );
}
Copy the code

I won’t go over all the differences, but I want you to see a slightly more complicated example. Hopefully, you’re starting to see the benefits of using Hooks. Let’s make one more change to this example and use usEffect to save our name to local storage so we don’t lose state when we refresh the page. Take a look at class-based components.

import React, { Component } from 'react';

export default class Greeting extends React.Component {
  constructor(props) {
    super(props);
    this.state = {
      firstName: window.localStorage.getItem('classFirstName') | |' ',
      lastName: window.localStorage.getItem('classLastName') | |' '
    };
    this.handleFirstNameChange = this.handleFirstNameChange.bind(this);
    this.handleLastNameChange = this.handleLastNameChange.bind(this);
  }
  handleFirstNameChange = (e) => this.setState({ firstName: e.target.value });
  handleLastNameChange = (e) => this.setState({ lastName: e.target.value });

  componentDidUpdate() {
    window.localStorage.setItem('classFirstName', this.state.firstName),
      [this.state.firstName];
    window.localStorage.setItem('classLastName', this.state.lastName),
      [this.state.lastName];
  }

  render() {
    return( <div> <input value={this.state.firstName} onChange={this.handleFirstNameChange} /><br /> <input value={this.state.lastName} onChange={this.handleLastNameChange} /> <p> <span>{this.state.firstName} {this.state.lastName}</span> </p> </div> ); }}Copy the code

In contrast, Hooks:

import React, { Component, useState, useEffect } from 'react';
export default function Greeting() {

  const [firstName, setFirstName] = useState(() =>
    window.localStorage.getItem('hooksFirstName') | |' '
  );
  const [lastName, setLastName] = useState(() =>
    window.localStorage.getItem('hooksLastName') | |' '
  );
  const handleFirstNameChange = (e) => setFirstName(e.target.value);
  const handleLastNameChange = (e) => setLastName(e.target.value);

  useEffect(() => {
    window.localStorage.setItem('hooksFirstName', firstName), [firstName];
    window.localStorage.setItem('hooksLastName', lastName), [lastName];
  });

  return (
    <div>
      <input value={firstName} onChange={handleFirstNameChange} /><br />
      <input value={lastName} onChange={handleLastNameChange} />
      <p>
        Hello, <span>{firstName} {lastName}</span>
      </p>
    </div>
  );
}
Copy the code

Hooks For Context

To better understand the Hooks’ other base hook, useContext, we need to take a closer look at the Context API, which is a feature released with React 16.3. As with most things, sometimes we have to fully understand another concept before we can move forward. If you are familiar with the Context API, you can skip this section. If you’re new to the Context API, we’ll go through it briefly and demo it. Add a context to your application first, otherwise you can’t use useContext. A good example of using a context is the profile component. Let’s think about what this component needs to have. When I log in to xyz.com, I have some data that NEEDS to be used in all or some of the components. Let’s assume we need two child components:

and

. One shows user information and pictures, and the other shows my team. We have members of the React, Angular, and Vue teams, so we will use these framework names as team names. Going back to the code, we need to pass the required data into the component via the component value property. In this way, we allow any component and its children to call this data. Let’s see how to implement this component by simply passing props to children (an option in the pre-Context API phase). When using the “prop pass-through” approach, data is passed layer by layer to each child component. This creates physical inputs for each component, allowing data (state) to flow externally to each component and its children.

import React from 'react';
import { render } from 'react-dom';
import './style.css';

const profileData = {
  company: 'Progress',
  companyImage: 'https://svgshare.com/i/9ir.svg',
  url: 'https://www.telerik.com/kendo-react-ui/',
  userImage: 'https://i.imgur.com/Y1XRKLf.png',
  userName: 'Kendoka',
  fullName: 'Kend not No Arikata',
  team: 'KendoReact'
}

const App = () => (
  <Profile data={profileData} />
)

const Profile = (props) => (
  <div className="profile">
    <img src={props.data.companyImage}/>
    <User data={props.data} />

  </div>
)

const User = (props) => {
  return (
    <div className="user">
      <a href={props.data.url}>
        <img src={props.data.userImage} width="138px" />
      </a>
      <h1 className="profile-userName">{props.data.userName}</h1>
      <p className="profile-fullName">({props.data.fullName})</p>
      <Team data={props.data} />

      
    </div>
  )
}

const Team = (props) => {
  return (
    <div className="team">
      <p className="profile-team">{props.data.team}</p>
    </div>
  )
}

render(<App />, document.getElementById('root'));
Copy the code

If an application has 10 components, each component has its own component tree, which in turn has another component tree. Would you like to manually pass props to components that may or may not need data? Or would you prefer to use that data from any point in the component tree? Context allows values to be shared between components without having to explicitly pass a prop at each level of the tree. Let’s look at how the Context API itself applies to class-based components:

import React from 'react';
import { render } from 'react-dom';
import './style.css';

const ProfileContext = React.createContext();
class ProfileProvider extends React.Component {
  state = {
    company: 'Progress',
    companyImage: 'https://svgshare.com/i/9ir.svg',
    url: 'https://www.telerik.com/kendo-react-ui/',
    userImage: 'https://i.imgur.com/Y1XRKLf.png',
    userName: 'Kendoka',
    fullName: 'Kend not No Arikata',
    team: 'KendoReact'
  }
  render() {
    return (
      <ProfileContext.Provider value={this.state}>
        {this.props.children}
      </ProfileContext.Provider>
    )
  }
}

const App = () => (
  <ProfileProvider>
    <Profile />
  </ProfileProvider>
)

const Profile = () => (
  <div className="profile"> <ProfileContext.Consumer> {context => <img src={context.companyImage} />} </ProfileContext.Consumer> <User /> </div> )  const User = () => ( <div className="user">
    <ProfileContext.Consumer>
      {context =>
        <React.Fragment>
          <a href={context.url}>
            <img src={context.userImage} width="138px" />
          </a>
          <h1 className="profile-userName">{context.userName}</h1>
          <p className="profile-fullName">({context.fullName})</p>
        </React.Fragment>
      }
    </ProfileContext.Consumer>
    <Team />
  </div>
)

const Team = () => (
  <ProfileContext.Consumer>
    {context =>
      <div className="team">
        <p className="profile-team">{context.team}</p>
      </div>
    }
  </ProfileContext.Consumer>
)

render(<App />, document.getElementById('root'));
Copy the code
Introduction to the Context API

We now need to learn how to get data state using useState, replace component lifecycle methods with useEffect, and improve providers using useContext. In the example of the Life Context API we share data using prop-passed methods. This is an effective method, but can be cumbersome in some cases. Or even better, use the Context API. Moving from a simple prop-passing example to a more maintainable Context API example, we ended up with code that, while a better way to solve our problem, further muddied our components, making them less reusable. Let’s see:

<ProfileContext.Consumer>




useState
useEffect
useContext

import React, { Component, useContext } from 'react';
import { render } from 'react-dom';
import './style.css';

// Look Ma, No Provider
const ProfileContext = React.createContext({
  company: 'Progress',
  companyImage: 'https://svgshare.com/i/9ir.svg',
  url: 'https://www.telerik.com/kendo-react-ui/',
  userImage: 'https://i.imgur.com/Y1XRKLf.png',
  userName: 'Kendoka',
  fullName: 'Kend not No Arikata',
  team: 'KendoReact'
});

const Profile = () => {
  const context = useContext(ProfileContext);
  return (
    <div className="profile">
      <img src={context.companyImage}/>
      <User />
    </div>
  )
}

const User = () => {
  const context = useContext(ProfileContext);
  return (
    <div className="user">
      <a href={context.url}>
        <img src={context.userImage} width="138px" />
      </a>
      <h1 className="profile-userName">{context.userName}</h1>
      <p className="profile-fullName">({context.fullName})</p>
      <Team />
    </div>
  )
}

const Team = () => {
  const context = useContext(ProfileContext);
  return (
    <div className="team">
      <p className="profile-team">{context.team}</p>
    </div>
  )
}

const App = () => <Profile />;

render(<App />, document.getElementById('root'));
Copy the code
There is no need to define a Provider

In the demo above, we introduced useContext, because we only read data from the context, and we pass the data directly to the createContext() method. This approach works, eliminating the need to wrap content with < Provider >. That said, it’s not the way I wanted to go – I do need to create a Provider in order to be able to change the Team properties. But I want to make it clear that if you pass in some data and don’t access any functions as we’d like, you can set it up without a provider. However, there are situations where we need to access and modify the state in the context, and we need to use providers. For example, I want to be able to change the team our users belong to. To do this, we need to create a Provider instead of just passing the default state data.

Update data using the Provider

Let’s go back to our previous example of using the Context API and using setState to update the data. We should be able to update the value of the Team property by calling a function that will be placed in our state as a key-value pair.

import React from 'react';
import { render } from 'react-dom';
import './style.css';

const ProfileContext = React.createContext();
class ProfileProvider extends React.Component {
  state = {
    company: 'Progress',
    companyImage: 'https://svgshare.com/i/9ir.svg',
    url: 'https://www.telerik.com/kendo-react-ui/',
    userImage: 'https://i.imgur.com/Y1XRKLf.png',
    userName: 'Kendoka',
    fullName: 'Kend not No Arikata',
    team: 'KendoReact',
    changeTeam: (team) => this.setState({
      team: `Kendo${team}`})}render() {
    return (
      <ProfileContext.Provider value={this.state}>
        {this.props.children}
      </ProfileContext.Provider>
    )
  }
}

const App = () => (
  <ProfileProvider>
    <Profile />
  </ProfileProvider>
)

const Profile = () => (
  <div className="profile"> <ProfileContext.Consumer> {context => <img src={context.companyImage} />} </ProfileContext.Consumer> <User /> </div> )  const User = () => ( <div className="user">
    <ProfileContext.Consumer>
      {context =>
        <React.Fragment>
          <a href={context.url}>
            <img src={context.userImage} width="138px" />
          </a>
          <h1 className="profile-userName">{context.userName}</h1>
          <p className="profile-fullName">({context.fullName})</p>
          <Team />
          <button className="profile-button"
            onClick={() => context.changeTeam('Angular')}>Angular</button>
          <button className="profile-button"
            onClick={() => context.changeTeam('Vue')}>Vue</button>
          <button className="profile-button"
            onClick={() => context.changeTeam('React')}>React</button> </React.Fragment> } </ProfileContext.Consumer> </div> ) const Team = () => ( <ProfileContext.Consumer>  {context => <div className="team">
        <p className="profile-team">{context.team}</p>
      </div>
    }
  </ProfileContext.Consumer>
)

render(<App />, document.getElementById('root'));
Copy the code

Let’s make sure that users can initiate changes in the team simply by pressing a button with the name of the team you want to switch to. We want this change to happen in the < User> component, but the button is displayed at the bottom of the Profile view.

changeTeam
setState


Context API

import React from 'react';
import { render } from 'react-dom';
import './style.css';

const ProfileContext = React.createContext();
class ProfileProvider extends React.Component {
  state = {
    company: 'Progress',
    companyImage: 'https://svgshare.com/i/9ir.svg',
    url: 'https://www.telerik.com/kendo-react-ui/',
    userImage: 'https://i.imgur.com/Y1XRKLf.png',
    userName: 'Kendoka',
    fullName: 'Kend not No Arikata',
    team: 'KendoReact',
    changeTeam: (team) => this.setState({
      team: `Kendo${team}`})}render() {
    return (
      <ProfileContext.Provider value={this.state}>
        {this.props.children}
      </ProfileContext.Provider>
    )
  }
}

const App = () => (
  <ProfileProvider>
    <Profile />
  </ProfileProvider>
)

const Profile = () => (
  <div className="profile"> <ProfileContext.Consumer> {context => <img src={context.companyImage} />} </ProfileContext.Consumer> <User /> </div> )  const User = () => ( <div className="user">
    <ProfileContext.Consumer>
      {context =>
        <React.Fragment>
          <a href={context.url}>
            <img src={context.userImage} width="138px" />
          </a>
          <h1 className="profile-userName">{context.userName}</h1>
          <p className="profile-fullName">({context.fullName})</p>
          <Team />
          <button className="profile-button"
            onClick={() => context.changeTeam('Angular')}>Angular</button>
          <button className="profile-button"
            onClick={() => context.changeTeam('Vue')}>Vue</button>
          <button className="profile-button"
            onClick={() => context.changeTeam('React')}>React</button> </React.Fragment> } </ProfileContext.Consumer> </div> ) const Team = () => ( <ProfileContext.Consumer>  {context => <div className="team">
        <p className="profile-team">{context.team}</p>
      </div>
    }
  </ProfileContext.Consumer>
)

render(<App />, document.getElementById('root'));
Copy the code

Let’s look at another example, including a button-like setup and a method to modify the TEAMS state. In fact, in this Hooks version there is no difference between the button syntax and the state object. The biggest advantage is to remove < profilecontext.consumer >, we just create a const in each function component, which will hold a reference to our context:

const context = useContext(ProfileContext);
Copy the code

We just need to invoke the context and its methods as before.


import React, { Component, useContext } from 'react';
import { render } from 'react-dom';
import './style.css';

const ProfileContext = React.createContext();

class ProfileProvider extends Component {
  state = {
    company: 'Progress',
    companyImage: 'https://svgshare.com/i/9ir.svg',
    url: 'https://www.telerik.com/kendo-react-ui/',
    userImage: 'https://i.imgur.com/Y1XRKLf.png',
    userName: 'Kendoken',
    fullName: 'Kendoken No Michi',
    team: 'KendoReact',
    toggleTeam: (team) => this.setState({
      team: `Kendo${team}`})}render() {
    return (
      <ProfileContext.Provider value={this.state}>
        {this.props.children}
      </ProfileContext.Provider>
    )
  }
}

let Profile = () => {
  const context = useContext(ProfileContext);
  return (
    <div className="profile">
      <img src={context.companyImage} />
      <User />
    </div>
  )
}

let User = () => {
  const context = useContext(ProfileContext);
  return (
    <div className="user">
      <a href={context.url}>
        <img src={context.userImage} width="138px" />
      </a>
      <h1 className="profile-userName">{context.userName}</h1>
      <p className="profile-fullName">({context.fullName})</p>
      <Team />
      <button className="profile-button"
        onClick={() => context.toggleTeam('Angular')}>Angular</button>
      <button className="profile-button"
        onClick={() => context.toggleTeam('Vue')}>Vue</button>
      <button className="profile-button"
        onClick={() => context.toggleTeam('React')}>React</button>
    </div>
  )
}

let Team = () => {
  const context = useContext(ProfileContext);
  return (
    <div className="team">
      <p className="profile-team">{context.team}</p>
    </div>
  )
}

class App extends Component {
  render() {
    return (
      <ProfileProvider>
        <Profile />
      </ProfileProvider>
    );
  }
}

render(<App />, document.getElementById('root'));
Copy the code

For another example, I’ll recompose our current profile component and “Change Team” button into their own separate components, and convert the component that provides the Context API into a function component – using useState instead of this.state. Notice that in the last example, we removed the profilecontext.consumer tag and introduced useContext. All of our components are now function components.

import React, { Component, useContext, useState } from 'react';
import { render } from 'react-dom';
import './style.css';

const ProfileContext = React.createContext();
const ProfileProvider = (props) => {
  const userInformation = {
    company: 'Progress',
    companyImage: 'https://svgshare.com/i/9ir.svg',
    url: 'https://www.telerik.com/kendo-react-ui/',
    userImage: 'https://i.imgur.com/Y1XRKLf.png',
    userName: 'Kendoken',
    fullName: 'Kendoken No Michi',
    team: 'KendoReact',
    toggleTeam: (property, value) => {
      setUserInfo( {... userInfo,[property]: value} ); } } const [userInfo,setUserInfo] = useState(userInformation);
  return (
    <ProfileContext.Provider value={userInfo}>
      {props.children}
    </ProfileContext.Provider>
  )
}

const Profile = () => {
  const context = useContext(ProfileContext);
  return (
    <div className="profile">
      <img src={context.companyImage} />
      <User />
    </div>
  )
}

const User = () => {
  const context = useContext(ProfileContext);
  return (
    <div className="user">
      <a href={context.url}>
        <img src={context.userImage} width="138px" />
      </a>
      <h1 className="profile-userName">{context.userName}</h1>
      <p className="profile-fullName">({context.fullName})</p>
      <Team />
      <ChangeTeam />
    </div>
  )
}

const Team = () => {
  const context = useContext(ProfileContext);
  return (
    <div className="team">
      <p className="profile-team">{context.team}</p>
    </div>
  )
}

const ChangeTeam = () => {
  const context = useContext(ProfileContext);
  return (
    <>
      <button className="profile-button"
        onClick={() => context.toggleTeam('team'.'Kendo for Angular')}>Angular</button>
      <button className="profile-button"
        onClick={() => context.toggleTeam('team'.'KendoVue')}>Vue</button>
      <button className="profile-button"
        onClick={() => context.toggleTeam('team'.'KendoReact')}>React</button>
    </>
  )
}

const App = () => (
  <ProfileProvider>
    <Profile />
  </ProfileProvider>
)

render(<App />, document.getElementById('root'));
Copy the code

Hooks for Reducers

In the previous sections we learned about a few basic React Hooks. Now, let’s apply what we’ve learned to a more advanced demonstration and learn to use the useReducer hook. Make sure you have some knowledge of useState before you do this. If you haven’t, go back to the previous section for an introduction. Redux is one of the most popular methods of dealing with unidirectional data using Reducer besides setState, and the React team encouraged Redux to manage state. However, since the release of version 16.9, React now has useReducer, which gives us a powerful way to useReducer without relying on the Redux library as a dependency to manage UI state.

Introduction to Reducers

Let’s discuss the differences between Redux state Reducer and the JavaScript method array.prototype.reduce. The summation function is a classic example of an array prototype. When we call reducer in an array that contains only numbers, we can return a number that adds up all the values in the array. The Reducer can enter an initial value as an optional second parameter. Let’s briefly take a look at some code that demonstrates the reduce method in JavaScript array. prototype.


    const votesByDistrict = [250, 515, 333, 410];
    const reducer = (accumulator, currentValue) => {
      return accumulator + currentValue;
    }

    console.log(votesByDistrict.reduce(reducer));
    // expected output: 1508

    // and below we simply add a value to start from:

    console.log(votesByDistrict.reduce(reducer, 777));
    // expected output: 2285
Copy the code

The sum function is the simplest example, but in this Reduce, you can do anything iteratively between curly braces. Think of it as a recipe. Given the same input, the same result is always produced. The method in question can be a pure function. This concept is important, especially when dealing with state management. Let’s look at one more reduce example to help us understand them better. Instead of accumulating a value in each iteration, you can compare it in each iteration. Just like summation, we store one value at a time, but instead of adding the values together, we store the highest value so far. With each iteration, we compare the next item in the array to the highest value so far, if it is large, it replaces it, if not, we continue iterating without updating the highest value.

Const todos = [{name: "Dishes", priority: 2}, {name: "laundry", priority: 3), {name: "homework", priority: 1}];let reducer = (highest, todo) => {
    returnhighest.priority > todo.priority ? Highest : todo; } todos.recuce(reduce, {}) // Output: {name: "laundry", priority: 3}Copy the code

This example demonstrates another way to use Reducer. You can do whatever you want in each iteration. The concept is simple: we take an array of items (in this case, objects), perform an operation and process it as a return value. The React Hooks Reducer is like a JavaScript array Reducer that returns a collection of something — in our case, the React state. Based on all previous and current actions and state changes that occurred in the past, our Reducer receives a state and an action as a parameter, the state is processed according to action.type, and we return the new state after running an instruction that matches the case of that particular action.type. Just as we cook something in real life, like a Bordeaux-style bordeaux sauce, we start with many ingredients: butter, scallions, veal, pepper, and of course wine. All these ingredients are mixed in a pan and then simmered. If we repeat and give the same steps, using the same ingredients, the same amount, the same stove and the same temperature, we should get the same results every time.

Overview of State and Actions

We will build a Todo application. First, we want the toDO list to have an initial TODO item that simply says “Get Started.”



Reducer
ADD_TODO
reducer
ADD_TODO



COMPLETE_TODO
TOGGLE_COMPLETE



reducer


    {
      id: 1,
      name: 'Get started',
      complete: false
    },
    {
      id: 2,
      name: 'Take a break',
      complete: false
    }
Copy the code

Notice that each item contains several attributes, one of which is ID. This is a key with a unique value that we use to locate a particular TOdo and change an attribute of that TOdo without affecting the value of the other toDO attributes. TOGGLE_COMPLETE is used to change the complete property from false to true. When this is done, any changes are propagated down to any components that use that state, triggering them to update. Since completed in the list starts with false, if we trigger the TOGGLE_COMPLETED method to update the item with ID 1, the status will look like this:


    {
      id: 1,
      name: 'Get started',
      complete: true // We changed it!
    },
    {
      id: 2,
      name: 'Take a break',
      complete: false
    }

Copy the code

Before Hooks were used, it was difficult to handle the Reducer operation without introducing third-party libraries. Now we can easily implement the Reducer mode in any React application without having to include other dependencies. This makes dealing with internal states easier than ever. This won’t replace all uses of Redux, but it gives React developers a clear, concise redux-style way to manage internal state instantly without installing any dependencies.

State management

Often in Redux, deciding how to classify and where to store state is one of the biggest problems for beginners. This was the first question in their Redux FAQ, and here’s what they said: There is no right answer. Some users prefer to keep every piece of data in Redux to maintain a fully serializable and controlled version of their application at all times. Others prefer to keep non-critical or UI state in the component’s internal state, such as “Is this drop-down list currently open?” Hooks are very powerful in the application layer, we can track things like “Yes drop open” and “Yes menu close”. We can properly manage UI data in a Redux-style manner without moving away from the React core. In a machine where the only responsibility is to constantly change and attach state, the Reducer is a different part of each operation. Its logic would increment a counter or manage a complex object whose changes would have an impact on the current state. Let’s access it from the feature component and set the state is the last part of the puzzle, and the first part of the new puzzle. Let’s look at how to manage a very simple Todo type application. This is a good example. Here are the rules for our Todo application. We will need to define some parts to design a simple real-world case using useReducer. We need to track state changes and updates by adding, completing, and clearing. Using the familiar Redux pattern, we typically associate each of these processes with a specific type of operation handled by the allocator:

  • A form field that allows us to enter tasks
  • An allocator that processes the form when we submit it
  • An actual task component that contains everything
  • One that handles state changesReducer

    Let’s start by adding and combining all of these pieces. I wouldn’t start by creating a React project, there are many ways to do it. I’ll provide some key code that you can copy and do whatever you want with. In our simple example, we will create the Todo component as the actual root component of the application, which looks like this:

    import React from 'react';
    import { render } from 'react-dom';
    import './style.css';

    const Todo = () => {
      return (
        <>
          Todo Goes Here
        </>
      );
    }

    render(<Todo />, document.getElementById('root'));

Copy the code

It includes a form and unsubmitted input fields. Some style sheets and JSON data have also been added. We can use it to embed toDO items to test how our list is rendered and the shape of our data conforms to HTML. You can (stackblitz.com/edit/todos- from here… Now that we’re done with the project, we’ll make our first change by importing the useReducer hook from React. Update the first line in the code.

    import React, { useReducer } from 'react';
Copy the code

Now we need to add the call to the useReducer, which takes state and action as input. We assign it to an array object, which is a tuple (two values) – this is destructing because useReducer () matches it as a return value: add the following line above the return statement of the Todo component:

const [todos, dispatch] = useReducer(todoReducer, initialState);
Copy the code

Items will be the actual list of toDO items, and Dispatch will be the actual reducer to change that list. In the return statement, we create a set of divs for each item in the Items array. There is another problem with our application because we have not yet created a function called todoReducer. Let’s add the code right below the line that sets the initialState assignment.


    const todoReducer = (state, action) => {
      switch (action.type) {
        case 'ADD_TODO': {
          return (action.name.length)
            ? [...state, {
              id: state.length ? Math.max(...state.map(todo => todo.id)) + 1 : 0,
              name: action.name,
              complete: false
            }]
            : state;
        }
        default: {
          returnstate; }; }}Copy the code

This seems complicated at first. All it does is set up a function to perform states and actions. Action. Type is also determined by switch. To start, we only have one operation, but we also want to set the default catch all, which will return the current state. But if it catches a real ADD_TODO, we return to the current state, expand, and append valid data to the end. The tricky part is assigning the new ID. What we do here is get the existing list of toDo and return the maximum ID plus 1, otherwise zero. Now that I’ve set up an initial state, we’re happy to move on to the next step. We need to make sure that when we type in the input field when we press Enter, the value we enter is sent to a function that will process it. So, first let’s replace div with the class name todo Input, as follows:


    <div className="todo-input">
      <form onSubmit={addTodo}>
        <input ref={inputRef} type="search" id="add-todo" placeholder="Add Todo..." />
      </form>
    </div>

Copy the code

This ensures that when we hit Enter, we send the form information to a function called addTodo (). We also refer to the input using the ref attribute and provide a reference value of inputRef for that element. With these updates, we need to do more. 1) We need to create a property called inputRef, which calls the useRef hook. 2) We need to create a function called addTodo (). Let’s start by creating the inputRef property. At the top of the TOdo component, add the following properties:

const inputRef = useRef();
Copy the code

We’ll use the ref attribute to get a reference to the input, which will allow us to access its value later. His reference will be supported by a local property in the todo function component, but this is just a call to the useRef hook. Call the created inputRef property, using inputref. value to get the input value. You need to import another hook just like we imported the useReducer.

import React, { useReducer, useRef } from 'react';
Copy the code

Finally, we need to create the addTodo () function that will use this reference and be responsible for assigning operations of type ADD_TODO. Add the following function directly above the return:


    function addTodo(event) {
      event.preventDefault();
      dispatch({
        type: 'ADD_TODO',
        name: inputRef.current.value,
        complete: false
      });
        inputRef.current.value = ' ';
    }

Copy the code

Inside the function, in case the page refreshes when we click submit. We call the preventDefault () method. We then use inputRef to get input values from the form to trigger the ADD_TODO operation. All toDO entries were originally completed false. Finally, we set the inputRef value to null. This clears the input field. Finally, we need one more update before ADD_TODO fires. Inside JSX, we are still mapping on initialState. We need to change from the following line:

{initialState.map((todo) => (
Copy the code

To:

{todos.map((todo) => (
Copy the code

We should now have a working useReducer hook that uses the addTodo function to dispatch operations to the todoReducer.

Add completed to-do items

Let’s also have a working example of USEffect in this project. Each time we check out a backlog, we update document.title to show the count or number of completed backlog items in the list. Right above the addTodo () function, let’s add logic to count how many todos we have completed. Then, when document.title changes, we need a useffect method to update it:


    const completedTodos = todos.filter(todo => todo.complete);
    useEffect(() => {
      // inputRef.current.focus();
      document.title = `You have ${completedTodos.length}items completed! `; })Copy the code

To do this, we also need to introduce hooks:

import React, { useReducer, useRef, useEffect } from 'react';
Copy the code

We are not done yet, we now need to add an event that will call the function that will dispatch the completed task. Add an onClick handler to div with the class name todo Name.


    <div className="todo-name" onClick={() => toggleComplete(todo.id)}>
      {todo.name}
    </div>
Copy the code

Next, we need a function to handle the click event. It’s as simple as sending a simple ID and operation type. Add this directly below the addTodo () function:


    function toggleComplete(id) {
      dispatch({ type: 'TOGGLE_COMPLETE', id });
    }
Copy the code

Finally, we added the following code to the todoReducer:


    case 'TOGGLE_COMPLETE': {
      returnstate.map((item) => item.id === action.id ? {... item, complete: ! item.complete } : item ) }Copy the code

I also set a style that we will add or remove depending on whether the full value of todo is true. Under the todos.map code, let’s change the line shown below:


    <div key={todo.id} alt={todo.id} className="column-item">

Copy the code

To:

<div className={`column-item ${todo.complete ? 'completed' : null}`}
  key={todo.id}>
Copy the code

We no longer need the Alt attribute, so we remove it. Now, when we click todo, it dispatches an operation and sets the completed value for that particular TOdo to true. Now, our filter will get this value through the usEffect method, which in turn updates document.title. We will also add the class name Completed, and the completed TODO will become opaque to represent the completed TODO. At this point, we have almost everything working except the delete function and the button to clear the list of all todos. To complete our demonstration, we’ll repeat what we’ve learned to make the last two functions work.

Delete a Todo entry

First, add an onClick () event to the close icon in todos HTML:


    <div className="todo-delete" onClick={() => deleteTodo(todo.id)}>
      &times;
    </div>
Copy the code

We’ll add action functions to handle these operations, it doesn’t have to be their own function, we can pass it directly from onClick (), or we can set up a similar switch statement to handle all assignments. We can take any approach we want. For demonstration purposes, I want to add them one by one. Now we create a function to handle dispatch:


    function deleteTodo(id) {
      dispatch({ type: 'DELETE_TODO', id });
    }

Copy the code

Now we just need to add a case to the Reducer’s switch statement to handle the Reduce. Here, we use the array’s.filter () method to remove a TOdo item from the list that satisfies the ID and return the status.


    case 'DELETE_TODO': {
      returnstate.filter((x) => x.id ! == action.id); }Copy the code
Clear all Todos

There’s nothing special about clearing todo, we just need to return an empty array. Here are three different pieces of code to do this. Add onClick () to the HTML button:


    onClick={() => clearTodos()}

Copy the code

Add a method to handle dispatch:


    function clearTodos() {
      dispatch({ type: 'CLEAR_TODOS' });
    }
Copy the code

Add a case to our Reducer method:


    case 'CLEAR_TODOS': {
      return [];
    }

Copy the code
Reducers summary

We have now built the foundation of the Todo application using useReducer. This pattern is useful when dealing with slightly more complex state at the data sublevel. We learned about pure functions and why they are at the heart of reducer, allowing us to return predictable state, which is now easier to implement in the core React library using this pattern.

Section 5: Customize React Hooks

Let’s learn how to create a custom React hook and all the rules you must keep in mind when using hooks. Hooks are only features! Any function can become Hooks. I don’t think the documentation on the ReactJS documentation site is simple enough. It’s not knocking them, I just feel like more people would benefit if I could try to explain it in a simpler way.

To Effect the hooks

If you know enough about basic Hooks, you can skip to creating custom Hooks. Without going back to all the basic Hooks, I think we just need to revisit one: the USEffect hook. I learned from reading Hooks in the ReactJS.org document that there are two ways to use useffect. Effects that do not need to be cleaned and effects that need to be cleaned. I hope that anyone using Hooks at this stage either knows these terms or takes a few minutes to read the official documentation. Before classes and Hooks were available, effect was placed in many lifecycle methods such as componentDidMount or componentDidUpdate. If there is duplicate code in both methods (performing the same processing and updating effects), we can now perform these operations in functional components with a single hook. Useffect tells React that our component needs to do something after the component is rendered. It runs after the first render and after each update. In my previous article, I only discussed the side effects of not cleaning up, so I want to quickly cover how to allow feature components to have side effects while cleaning up. Here’s an example of how to run usEffect without any cleanup:


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

If you do need to clean up to run, you can return the function from useffect. This is optional and allows you to run some code after the effect and before any new effects run. Subscribing to some content may require unsubscribing as part of the effect cleanup process. React will perform this cleanup during uninstallation.


    useEffect(() => {
      console.log("Subscribe to Something); return function cleanup() { console.log("Unsubscribe to Something);
      };
    });

Copy the code

The above effects will be run more than once per render. React cleans the effect of the previous render before running the effect of the next render. This should be noted. See the ReactJS documentation for an explanation of why hooks are run with every update. However, keep in mind that if this behavior causes performance problems, you can opt out. We can also optimize performance by skipping effects with optional parameters. For example, maybe we don’t want to run subscribe/unsubscribe unless some id has been changed. Take a look at the example below to see how to do this, it’s pretty simple!


    useEffect(() => {
      console.log("Subscribe to Something); return () => { console.log("Unsubscribe to Something);
      };
    }, [props.something.id]); // only if something.id changes

Copy the code

Hooks, especially USEffect, now allow you to split code based on what it is doing rather than what lifecycle method it is in. When we only have class and lifecycle methods, we sometimes have to mix concerns. Now, with multiple USEffect methods, React can apply each effect in the order specified. This is a huge benefit for organizing your code in your application.

Create custom hooks

I really liked a recent post by Adam Rackis on Twitter: “Hooks are far better at writing than anything we’ve seen.” The thing I want you to know about Hooks is that all the great changes we saw in the class, and how we have so many combination options, are now in Hooks. This means that our hands are now free when it comes to the composition of the functional components in React. This is a huge step forward for React developers. Custom hooks are JavaScript functions whose names are prefixed with single use. A custom hook is a normal function, but we use a different standard. By adding the word use at the beginning, we know that this function follows the rules of Hooks. With a better understanding of hooks, let’s take what we know as a simple piece of code, update our document title, and create a simple custom Hook. It seems that we need to do something on several pages or in many different functional components in the application. When the information changes, we want to update the document title with some type of string. Also, we don’t want to repeat this logic in every functional component. We’ll start by extracting this code into hooks locally on the same page, then see how to import the same hook into multiple components and locate it together. Easy, right? If this is true, then our custom hooks could also call one of the React Core base hooks, such as USEffect. Let’s look again at the functional component that updates the document title.


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

    function Counter() {
      const [count, setCount] = useState(0);
      const incrementCount = () => setCount(count + 1);
      useEffect(() => {
        document.title = `You clicked ${count} times`});return (
        <div>
          <p>You clicked {count} times</p>
          <button onClick={incrementCount}>Click me</button>
        </div>
      )
    }

    export default Counter;

Copy the code

Therefore, we want to create a custom hook here, pass a piece of text to the hook, and the hook will update the document title for us. Let’s first look at the code needed to create this custom hook:


    const useDocumentTitle = (title) => {
      useEffect(() => {
        document.title = title;
      }, [title])
    }

Copy the code

Up here you can see that what we need as an argument to this hook is a string, which we call title. Inside the hook, we call the React Core basic USEffect hook and set the title. The second parameter to usEffect will perform this check for us and update the title only if its local state is different from what we passed in. You mean, creating custom hooks is as easy as creating functions? Yes, its core is very simple, and the function can reference any other hook. Damn it… Creating custom hooks is easier than we thought! Let’s review what our overall functional components look like today. You’ll see that I commented out the old call to USEffect, and here’s how we used the custom hook instead.


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

    const useDocumentTitle = title => {
      useEffect(() => {
        document.title = title;
      }, [title])
    }

    function Counter() {
      const [count, setCount] = useState(0);
      const incrementCount = () => setCount(count + 1);
      useDocumentTitle(`You clicked ${count} times`);
      // useEffect(() => {
      //   document.title = `You clicked ${count} times`
      // });

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

    export default Counter;

Copy the code

Let’s clean it up a bit further and see how we can use this hook if it is provided by some NPM package, rather than being copied and pasted at the top of a file.


    import React, { Component, useState } from 'react';
    import useDocumentTitle from '@rehooks/document-title';

    function Counter() {
      const [count, setCount] = useState(0);
      const incrementCount = () => setCount(count + 1);
      useDocumentTitle(`You clicked ${count} times`);

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

    export default Counter;

Copy the code

This is great, but I also want you to notice that I don’t have to import USEffect in my feature because the hooks we imported from “@rehooks/ Document title” will handle this. So if I don’t need usEffect, I can omit it from the component import. I hope this illustrates the basics of creating custom React hooks, and you can even see its power with a simple example like this.

Manages the control state of the KendoReact component

Hooks are great for handling specific types of application state. Examples are control state, local component state, and session state. Using KendoReact UI(www.telerik.com/kendo-react…) Component I want to use Hooks, but I want to start simple. Instead of using classes, we will refactor to use function components. We will look for instances that demonstrate the use of this.state and this.setState, because when we convert a component to a function, we will no longer need to use the this keyword, and we will not need to use the constructor or call setState. So let’s start refactoring the KendoReact demo to show how to use our KendoReact dialog. If you look at the following presentation of the main. JSX (stackblitz.com/edit/kendor…). Page, we can identify several target areas that change when using function components and React hooks. Code and lines highlighted in green need to be modified, and lines highlighted in red can be removed entirely.

  1. In line 6 there is a class definition that we need to convert into a function component
  2. There is a constructor at line 7 that is called at line 8super()Method, line 10 has some bindings. None of this is needed in a function component that uses Hooks.
  3. In line 9, we create a state instance and give it a default value of true, which will be trueuseStateThe hook call.
  4. In line 13, we need to rename ittoggleDialogFunction and switch it to the ES6 arrow function-style syntax, just called on lines 14 through 16UseState ()Assignment provides an update methodsetVisibleThe value it will reference will be visible instead ofthis.state.visible.
  5. In line 19, we have oneRender ()Call, which is not necessary in function components
  6. We mention this in lines 22, 23, 26 and 27. whilethis.stateNeed to quotevisibleandtoggleVisibleRather thantoggleDialog, I’ll explain why you want to rename this function later.

    The first thing to do is to turn the component into a function component and remove itconstructorConstructor, deletesupr()Reference andtoggleDialog()Function binding. There are several syntax options available here, and I prefer the ES6 arrow function style:
const multiply = (x, y) => { return x * y };
Copy the code

In our component, line 6 now looks like this:


    const DialogWrapper = () => {

Copy the code

Let’s set a hook instead of a state object. Instead of creating an object named state, we’ll set up a call to useState () and deconstruct its return value into a variable that will hold our state and update/set methods to update that state. Our state name will be visible, and its update method will be called setVisible. We’ll delete the entire constructor and replace it with this line:

const [visible, setVisible] = useState(true);
Copy the code

Since we are using the useState () base hook, we also need to import it. Our React import now looks like:


    import React, { useState } from 'react';

Copy the code

Next, we need a function that calls setVisible in this component to switch its value. We’ll name it toggleVisible, not toggleDialog, because we’re in a feature component, so the syntax we used before won’t work. Instead, I’ll update it to the ES6 arrow function style. This function simply sets the visible state to the opposite of the current state.


    const DialogWrapper = () => {;
      const [visible, setVisible] = useState(true);
      const toggleVisible = () => setVisible(! visible);Copy the code

Now we need to remove the Render () block and its two braces. In addition, we need to remove all references to this.toggleDialog and this.state.visible and change them to toggleVisible and Visible accordingly. Now in return (), we will make the following changes:


    return (
      <div>
      <Button className="k-button" onClick={toggleVisible}>Open Dialog</Button>
      {visible && <Dialog title={"Please confirm"} onClose={toggleVisible}>
        <p style={{ margin: "25px", textAlign: "center" }}>Are you sure you want to continue? </p> <DialogActionsBar> <Button className="k-button" onClick={toggleVisible}>No</Button>
        <Button className="k-button" onClick={toggleVisible}>Yes</Button>
        </DialogActionsBar>
      </Dialog>}
      </div>
    );
Copy the code

Also, we just updated the code in return () to not reference the this keyword and to use the new function name toggleVisible. We have successfully converted the KendoReact demo to use functional components and basic useState hooks. Let’s use a great tool called Githrisk to see what our overall change looks like:

conclusion

I hope this guide helps you better understand the basics of Hooks and allows you to build new and amazing things out of these examples. If it works for you, please share and spread it. I want to give you a good understanding of the creation of Hooks, which I think can best be explained by reviewing Sophie Alpert’s talk at React Conf 2018. In the past, some React developers have encountered confusion about when to use and when not to use classes. The problem dates back several years, in one case, to an article by Dan Abramoff: How to use the React class to sleep at night. Although we may sometimes use them in the current React or encounter them in the future when dealing with legacy code, this issue is being addressed now and we have seen that developers have strong opinions and mostly use functional components. Sophie Alpert has a great question when talking about what the React team is doing to make it easier to build great UIs and improve the developer experience in React. Why React still sucks? Here are the answers to the famous talk at React Conf 2018:

Reuse logic

Prior to React Hook, we used a lot of higher-order components and render props to achieve this, which would require you to frequently rebuild the application when using these patterns and lead to packaging hell (doomsday pyramid-style nesting).

Huge parts

Chaos often occurs in our components due to the fragmentation of logical pieces in different lifecycle methods.

Confusion class

This is the first of many quotes I will leave you with. Lessons are hard for humans, but not just humans, lessons are hard for machines — Sophie Albert Understanding classes in JavaScript can be tricky, and before hooks, there was the need to use class components to access state and lifecycle methods. Simply defining a class component requires quite a bit of boilerplate. Hooks help solve these problems, and for that reason I’d like to leave you with a few other notable quotes from our fearless response and community leaders! Hooks allow you to always use functions instead of switching between functions, classes, HOC, and render items — Dan Abramov If you want to make the world a better place, look at React Hooks and make the changes. – Michael Jackson Hooks offer a new way to deal with React problems – Dave Ceddia with React Hooks, we have the best of both worlds: Clean functional components that can use states — The David Katz hook is React, which is where React goes, React V2 — The Michael Jackson React Hook radically simplifies the way I create, write, read, and prototype components — Zach Johnson These are some of the things people are saying about React Hooks.

If you want to know more front-end knowledge, please pay attention to my public account “front-end Notepad”