React

React Hooks Explained – Functional Components With State


Subscribe On YouTube

DemoCode

React Hooks is a new feature which is coming with React 16.7 and is adding missing pieces of functionality to React’s functional components:

  • By using State Hooks it’s possible to add state to functional components
  • By using Effect Hooks you can add code to functional components which is executed in response to component’s lifecycle events

In this tutorial we’ll explore both, State Hooks and Effect Hooks in a very practical way. By building real-world sample application from scratch you’ll be able to understand the concept and learn how you can apply React Hook in your application.


If you like CodingTheSmartWay, then consider supporting us via Patreon. With your help we’re able to release developer tutorial more often. Thanks a lot!


You can find the details of the React Hooks proposal at https://reactjs.org/docs/hooks-intro.html:
00.png

Getting Started

To get started first let’s set up a new React project from scratch using the create-react-app script. You do not need to install create-react-app on your system, you can directly execute the script by using the npx command:

$ npx create-react-app my-react-hooks-app

Executing the command in this way is creating a new project folder my-react-hooks-app and in this folder you’ll find the initial React project setup. Change into the newly created folder:

$ cd my-react-hooks-app

Because the React Hooks feature is not released yet, we need to make sure to at least install the version 16.7.0-alpha.2 of the packages react and react-dom.

$ npm i react@next react-dom@next

After having executed this command please check in the project’s package.json file that the correct version of both package is listed in the dependencies section. Once React version 16.7 is released this addtional installation step is no longer needed.

Because we’ll also be making using a libraray named Material-UI a last installation step needs to be completed:

$ npm install @material-ui/core

This libraray is containing React components that implement Google’s Material Design. We’ll be making use of those components to implement the user interface of the sample application of this tutorial.

Finally let’s check if the default project setup is working as expected by starting the development web server:

$ npm start

Once the server is started up successfully you should be able to see the following result in the browser when accessing URL http://localhost:3000:

01.png With this default React application in place we’re now ready to add the State Hook.

Using State Hooks

Delete the default code which is available in App.js and start with adding the following import statement on top:

import React, { useState } from 'react';

In order to be able to make use of the new State Hook feature we need to import useState from the react package. Next add the following import statements as well:

import Card from '@material-ui/core/Card';
import CardActions from '@material-ui/core/CardActions';
import CardContent from '@material-ui/core/CardContent';
import CardMedia from '@material-ui/core/CardMedia';
import Typography from '@material-ui/core/Typography';
import Button from '@material-ui/core/Button';
import Grid from '@material-ui/core/Grid';
import Icon from '@material-ui/core/Icon';

Those import statement will give us access to material design components of the Material-UI library.

Next let’s add App compoment as a functional component by using the following lines of code:

function App() {
}

export default App;

Now let’s add state to our functional component by using useState:

const [courses, setCourses] = useState([
{
title: "Server Side Rendering with React and Redux",
description: "Build React, Redux, and React Router apps using Server Side Rendering (SSR), Isomorphic, and Universal JS techniques",
url: "https://codingthesmartway.com/courses/react-redux-ssr/",
courseImage: 'react01.png',
upvote_count: 0,
downvote_count: 0
},
{
title: "React - The Complete Guide",
description: "Dive in and learn React from scratch! Learn Reactjs, Redux, React Routing, Animations, Next.js basics and way more!",
url: "https://codingthesmartway.com/courses/react-complete-guide/",
courseImage: 'react02.png',
upvote_count: 0,
downvote_count: 0
},
{
title: "The Complete React Web Developer Course (with Redux)",
description: "Learn how to build and launch React web applications using React v16, Redux, Webpack, React-Router v4, and more!",
url: "https://codingthesmartway.com/courses/react-complete/",
courseImage: 'react03.png',
upvote_count: 0,
downvote_count: 0
}
]);

Here you can see that useState is taking the array of data objects which should be put into the component’s state as an argument. What is being returned by the call of useState is the state object itself (courses) and a function which can be used to set the state (setCourses). We’re using array destructuring syntax here to split the return value and save it in courses and setCourses at the same time.

As you can see each course data object is consisting of six properties: title, description, url, courseImage, upvote_count and downvote_count. The user will be able to up or down vote each course and thereby increase the values of properties upvote_count and downvote_count. Incrementing those values means setting a new state which we can do by calling setCourse. To do so let’s add two functions: upvoteCourse and downvoteCourse to our App component:

const upvoteCourse = index => {
  const newCourse = [...courses];
  newCourse[index].upvote_count++;
  setCourses(newCourse);
};

const downvoteCourse = index => {
  const newCourse = [...courses];
  newCourse[index].downvote_count++;
  setCourses(newCourse);
};

To complete the implementation of App component let’s add the corresponding JSX code as part of the return statement:

  return (
    <div className="app">
      <NavBar />
      <div>
        <Grid container spacing={24} style={{padding: 24}}>
          {courses.map((course, index) => (
            <Grid item xs={12} sm={12} lg={4} xl={3}>
              <Course 
                key={index} 
                index={index} 
                course={course} 
                upvoteCourse={upvoteCourse}
                downvoteCourse={downvoteCourse}
                />
            </Grid>
          ))}
        </Grid>
      </div>
      <Footer />
    </div>
  )

Here we’re iterating through the list of courses available in the component’s state property which is available via courses. The output for each course is done by another component: Course. So let’s add the implementation of that component to App.js as well:

function Course({ course, index, upvoteCourse, downvoteCourse }) {
  return (
    <div>
      <Card style={{maxWidth: '500px', marginBottom: '10px'}}>
        <CardMedia style={{height: 0, paddingTop: '56.25%'}}
                    image={ course.courseImage }
                    title={ course.title }
                    />
        <CardContent>
          <Typography variant="headline" component="h2">{ course.title }</Typography>
          <Typography component="p" color="textSecondary">{ course.description }</Typography>
          <br />
          <Typography color="textSecondary">{ course.upvote_count }
          <Icon color="primary" onClick={() => upvoteCourse(index)}>
            thumb_up_alt
          </Icon>
          
          &nbsp;&nbsp;
          <span>{ course.downvote_count }</span>
          <Icon color="primary" onClick={() => downvoteCourse(index)}>
            thumb_down_alt
          </Icon>
          </Typography>
        </CardContent>
        <CardActions>
          <Button size="small" href={ course.url } target="_blank">Go To Course</Button>
        </CardActions>
      </Card>
    </div>
  )
}

As parameters we’re passing in course, index, upvoteCourse and downvoteCourse. The output is done by making use of Material Design components from the Material-UI library.

Finally let’s take a look at the complete code in App.js:

import React, { useState } from 'react';
import Card from '@material-ui/core/Card';
import CardActions from '@material-ui/core/CardActions';
import CardContent from '@material-ui/core/CardContent';
import CardMedia from '@material-ui/core/CardMedia';
import Typography from '@material-ui/core/Typography';
import Button from '@material-ui/core/Button';
import Grid from '@material-ui/core/Grid';
import Icon from '@material-ui/core/Icon';

function Course({ course, index, upvoteCourse, downvoteCourse }) {
  return (
    <div>
      <Card style={{maxWidth: '500px', marginBottom: '10px'}}>
        <CardMedia style={{height: 0, paddingTop: '56.25%'}}
                    image={ course.courseImage }
                    title={ course.title }
                    />
        <CardContent>
          <Typography variant="headline" component="h2">{ course.title }</Typography>
          <Typography component="p" color="textSecondary">{ course.description }</Typography>
          <br />
          <Typography color="textSecondary">{ course.upvote_count }
          <Icon color="primary" onClick={() => upvoteCourse(index)}>
            thumb_up_alt
          </Icon>
          
          &nbsp;&nbsp;
          <span>{ course.downvote_count }</span>
          <Icon color="primary" onClick={() => downvoteCourse(index)}>
            thumb_down_alt
          </Icon>
          </Typography>
        </CardContent>
        <CardActions>
          <Button size="small" href={ course.url } target="_blank">Go To Course</Button>
        </CardActions>
      </Card>
    </div>
  )
}

function App() {
  const [courses, setCourses] = useState([
    {
      title: "Server Side Rendering with React and Redux",
      description: "Build React, Redux, and React Router apps using Server Side Rendering (SSR), Isomorphic, and Universal JS techniques",  
      url: "https://codingthesmartway.com/courses/react-redux-ssr/",
      courseImage: 'react01.png',
      upvote_count: 0,
      downvote_count: 0
    },
    {
      title: "React - The Complete Guide",
      description: "Dive in and learn React from scratch! Learn Reactjs, Redux, React Routing, Animations, Next.js basics and way more!",  
      url: "https://codingthesmartway.com/courses/react-complete-guide/",
      courseImage: 'react02.png',
      upvote_count: 0,
      downvote_count: 0
    },
    {
      title: "The Complete React Web Developer Course (with Redux)",
      description: "Learn how to build and launch React web applications using React v16, Redux, Webpack, React-Router v4, and more!",  
      url: "https://codingthesmartway.com/courses/react-complete/",
      courseImage: 'react03.png',
      upvote_count: 0,
      downvote_count: 0
    }
  ]);

  const upvoteCourse = index => {
    const newCourse = [...courses];
    newCourse[index].upvote_count++;
    setCourses(newCourse);
  };

  const downvoteCourse = index => {
    const newCourse = [...courses];
    newCourse[index].downvote_count++;
    setCourses(newCourse);
  };

  return (
    <div className="app">
      <NavBar />
      <div>
        <Grid container spacing={24} style={{padding: 24}}>
          {courses.map((course, index) => (
            <Grid item xs={12} sm={12} lg={4} xl={3}>
              <Course 
                key={index} 
                index={index} 
                course={course} 
                upvoteCourse={upvoteCourse}
                downvoteCourse={downvoteCourse}
                />
            </Grid>
          ))}
        </Grid>
      </div>
      <Footer />
    </div>
  )
}

export default App;

You should be able to see the following output in the browser: