Harnessing React Hooks, a practical example
Tags: react, javascript, beginners, functional
Published: 2019-06-10 00:50:31

1. [Introduction](#introduction "Introduction")
2. [Project Setup](#project-setup "Project Setup")
3. [Dependencies](#dependencies "Dependencies")
4. [useState](#usestate "useState")
5. [useEffect](#useeffect "useEffect")
6. [Project Links](#project-links "Project Links")
6. [Resources](#resources "Resources")

## Introduction
#### Prerequisites: ```This article is for people who are familiar with the basic concepts of react. ```

Hooks are a powerful feature in the react library that combines react concepts like props, state, context, refs and life-cycle. This feature is supported in React 16.8.0 and above. Hooks were developed for:

1. Simplicity
2. Performance

Prior to the advent of hooks, one could only declare state in react class components. Besides whenever `stateful components` were mentioned in react the only thing that came to mind was a `class component` while `functional components` were regarded as `stateless components` but this is no more the case. Thanks to react hooks `functional components` can now declare state and any other react concepts you can think of. Thus react hooks can best be described as follows:

`
Hooks are functions that let you “hook into” React state and lifecycle features from functional components.
`

This brings a new distinction to these terms:
* `Stateful Components`: These are class components or functional components that declare and manage state. They are usually `parent-components`
* `Stateless Components`: These are class components or functional components that do not declare or manage state. They are usually `child-components`

Though the [react documentations](https://reactjs.org/docs/hooks-intro.html) on hooks are well detailed I strongly believe that the best way to grasp a new concept is by doing which is why I have cooked up the mini-project we'll be working on in this article.

### Project Setup

To show you how to harness react hooks we'll be building an `Instagram clone` together. Below is a live demo of the project

![Demo](https://thepracticaldev.s3.amazonaws.com/i/473z36leefa0jrla5ycd.gif)
`I hope you're as excited as I am`

We'll be using `create-react-app` for this project. So for starters open you command line and type the following:

```
npx create-react-app instagram-app

```

Now cd into the instagram-app folder we created and install the following dependencies

```
cd instagram-app
npm install faker moment styled-components uuid

```
#### Dependencies

* `faker` is an npm package that generates random data inputs
* `moment` is an npm package used for date formatting
* `styled-components` is an npm package that we'll use to style our components. It utilizes tagged template literals to style your components and eliminates the need for creating CSS files in our project.
* `uuid` this is random uuid generator


#### Now we're going to create our component folders
in your command line type the following
```
cd src
mkdir -p component/Form component/Login component/PostContainer component/SearchBar component/CommentSection component/Authentication
```

this creates the following folders in our project

![Screenshot 2019-06-08 at 11.58.41 PM.png](https://ucarecdn.com/6a60bfef-8a86-4548-bcb6-0b52d5d378dc/)

#### Lets flesh out our components

while in your src folder and type the following

```
touch component/PostContainer/PostContainer.js component/Form/Form.js component/Login/Login.js component/SearchBar/SearchBar.js component/CommentSection/CommentSection.js
```

This will create a js file in each component directory respectively.


Since this article is focused on react hooks and its implementation, I'll be going over the code snippets where hooks were used. Which are
* App.js
* PostContainer.js
* Login.js

The link to the complete project repo and hosted app can be found below:

[Instagram-clone](https://github.com/PascalUlor/Instagram-clone)

[Instagram-clone-netlify](https://5cfd8594d7a4c562852fee2e--admiring-shaw-fdc467.netlify.com/)


The react hooks we'll be using into in this project are the `useState` and `useEffect` hooks.

## useState

This is called in a `functional component` to add some local state to it. This allows us to reuse and share stateful logic in our application.

## useEffect
This gives functional components the ability to perform side effects in much the same way as `componentDidMount`, `componentDidUpdate` and `componentWillUnmount` method act in class components.

To use state in our react we must import them as thus:
```javascript
import React, { useState, useEffect } from "react";
```

In our App.js file make the following changes

```javascript
import React, { useState, useEffect } from "react";
import styled from "styled-components";
import uuidv4 from "uuid/v4";
import data from "./dummy-data";
import SearchBar from "./component/SearchBar/SearchBar";
import PostContainer from './component/PostContainer/PostContainer';

const preprocessData = data.map(post => {
  return {
    ...post,
    postId: uuidv4(),
    show: "on"
  };
});

function App() {
  const [posts, setPost] = useState([]);
  const [search, setSearch] = useState("");

  useEffect(() => {
    const allData = localStorage.getItem("posts");
    let postData;
    if (allData) {
      postData = JSON.parse(allData);
    } else {
      localStorage.setItem("posts", JSON.stringify(preprocessData));
      postData = JSON.parse(localStorage.getItem("posts"));
    }
    setPost(postData);
  }, []);

const handleSearch = e => {
    e.preventDefault();
    const data = posts;
    setSearch(e.target.value.trim());
      const query = data.map(post => {
        if (!post.username.trim().toLowerCase().includes(e.target.value.trim())) {
          return {
            ...post,
            show: "off"
          };
        }
        return {
          ...post,
          show: "on"
        };
      });
      setPost(query);
  };

  return (
    <AppContainer>
      <SearchBar />
      <PostContainer />
    </AppContainer>
  );
}

export default App;


```

#### Explanation
1. In our App.js file, we imported our raw `data` and tweaked it a bit with the following lines of code

```javascript
const preprocessData = data.map(post => {
  return {
    ...post,
    postId: uuidv4(),
    show: "on"
  };
});

```
All this does is give each post in our dummy data a postId and a `show` property. We also imported the react hooks we'll be needing

```javascript
import React, { useState, useEffect } from "react";
```

2. Inside our App component, we initialized our state. `Note` the syntax.

```javascript
  const [posts, setPost] = useState([]);
  const [search, setSearch] = useState("");
```
* `useState` returns a pair of values that represents the `current-state` (posts) and the `update-function` that updates the state (setPost and setSearch). `setPost` and `setSearch` respectively are similar to the `this.setState` method of `class components`.

`
"The key difference between the `this.setState` method of class components and the update-function of the useState react hook is that it does not merge the old state with the new state"
`
The useState() method take an argument which is the `initial state` (i.e useState([]),useState("")) and is only used in the first render. The argument can be anything from null, a string, a number or an object.

3. Next, we handle some side effects. Much like the `componentDidMount` of class components, we will use the `useEffect` function to mount and render our data from `localStorage` to state
```javascript
useEffect(() => {
    const allData = localStorage.getItem("posts");
    let postData;
    if (allData) {
      postData = JSON.parse(allData);
    } else {
      localStorage.setItem("posts", JSON.stringify(preprocessData));
      postData = JSON.parse(localStorage.getItem("posts"));
    }
    setPost(postData);
  }, []);
```

* `useEffect` takes two arguments. The `callback function` that handles the side effects and an array of the states the effect would have to react to. It is much like adding an event listener to a piece of state.
In the above effect, we inputted an empty array as the second argument because we want to call this effect only once when the application starts (just like componentDidMount). If no array is specified the component will rerender on every state change.

Now we need to pass this state to our child components as props.
Make the following update to the JSX of our App.js file

```javascript
return (
    <AppContainer>
      <SearchBar search={search} handleSearch={handleSearch} />
      {posts.map((userPost, index) => {
        return <PostContainer 
        key={index} 
        props={userPost} 

        />;
      })}
    </AppContainer>
  );
```
Now PosContainer.js and SearchBar.js need to render the states they have received as props.

In our PostContainer.js file, we'll harness react hooks ability to reuse stateful logic without changing our component hierarchy.

PostContainer.js

```javascript

const PostContainer = ({ props }) => {
    const {
      postId,
      comments,
      thumbnailUrl,
      imageUrl,
      timestamp,
      likes,
      username,
      show
    } = props;
    const commentDate = timestamp.replace(/th/, "");
    const [inputValue, setInputValue] = useState("");
    const [inputComment, setInputComment] = useState(comments);
    const [createdAt, setCreatedAt] = useState(
      moment(new Date(commentDate), "MMM D LTS").fromNow()
    );
  
    const [addLikes, updateLikes] = useState(likes);
    
    useEffect(()=>{
      const post = JSON.parse(localStorage.getItem("posts"));
      const postUpdate = post.map((userPost) => {
        if(postId === userPost.postId) {
          return {
            ...userPost, comments: inputComment, timestamp: `${moment(new Date(), "MMM D LTS")}`, likes: addLikes
          }
        }
        return userPost;
      });
      localStorage.setItem("posts", JSON.stringify(postUpdate));
    },[inputComment, postId, createdAt, addLikes])
  
    const handleChange = e => {
      setInputValue(e.target.value);
    };
    const postComment = e => {
      e.preventDefault();
      const newComment = {
        postId: postId,
        id: uuidv4(),
        username: faker.name.findName(),
        text: inputValue
      };
      setInputComment([...inputComment, newComment]);
      setInputValue("");
      setCreatedAt(moment(new Date(), "MMM D LTS").fromNow());
    };
    const handleLikes = () => {
      let newLike = likes;
      updateLikes(newLike + 1);
    };
    
  
    return (
      <PostContainerStyle display={show}>
        <UserDeets>
          <UserThumbnail src={thumbnailUrl} alt="user-profile" />
          <p>{username}</p>
        </UserDeets>
        <UserPostArea>
          <PostImage src={imageUrl} alt="user-post" />
        </UserPostArea>
        <Reaction>
          <PostIcons>
            <span onClick={handleLikes}>
              <IoIosHeartEmpty />
            </span>
  
            <span>
              <FaRegComment />
            </span>
          </PostIcons>
          {addLikes} likes
        </Reaction>
        {inputComment.map(comment => {
          return <CommentSection key={comment.id} props={comment} />;
        })}
        <TimeStamp>{createdAt}</TimeStamp>
        <Form
          inputValue={inputValue}
          changeHandler={handleChange}
          addComment={postComment}
        />
      </PostContainerStyle>
    );
};

export default PostContainer;

```
#### Explanation
* `Note` that in our PostContainer component the props we received from App.js were rendered as states using the `useState` hook.

```javascript
onst commentDate = timestamp.replace(/th/, "");
    const [inputValue, setInputValue] = useState("");
    const [inputComment, setInputComment] = useState(comments);
    const [createdAt, setCreatedAt] = useState(
      moment(new Date(commentDate), "MMM D LTS").fromNow()
    );
  
    const [addLikes, updateLikes] = useState(likes);
```

* We also used the `useEffect` hook to manage stateful logic and persist our state updates to `localStorage`.

```javascript

useEffect(()=>{
      const post = JSON.parse(localStorage.getItem("posts"));
      const postUpdate = post.map((userPost) => {
        if(postId === userPost.postId) {
          return {
            ...userPost, comments: inputComment, timestamp: `${moment(new Date(), "MMM D LTS")}`, likes: addLikes
          }
        }
        return userPost;
      });
      localStorage.setItem("posts", JSON.stringify(postUpdate));
    },[inputComment, postId, createdAt, addLikes])

```
In the `useEffect` hook above note the second argument which is an array of states that can trigger the `useEffect` function.
```javascript

[inputComment, postId, createdAt, addLikes]
```
This means that any change to any of these states will cause the state to be updated in `localStorage`.

At this point, our posts should be rendered on the browser like so:

![posts](https://thepracticaldev.s3.amazonaws.com/i/0fudm3er4harl25d9alg.gif)

* The `handleChange` function calls the `setInpuValue` function to handle the state of the form input field just like the `this.setState` method of class components. While the `handleLikes` function calls the `updateLike` function to add likes

* The `postComment` adds a comment to each post and update the date by calling the `setComment` and `setCreatedAt` function respectively.


Wow! Wasn't that fun. Now we can `Add comments` and `Add Likes` and persist our changes to `localStorage`

It's time to work on our Login component and create our higher order component for `authentication`

Login.js

```javascript
const Login = ({ props }) => {
    const [userInput, setUserInput] = useState({
      username: "",
      password: ""
    });
    const [loggedIn, setloggedIn] = useState(false);
  
    useEffect(() => {
      setloggedIn(true);
    }, [userInput.username, userInput.password]);
    const loginHandler = () => {
      let logDeets = {
        username: userInput.username,
        password: userInput.password,
        loggedIn: loggedIn
      };
      localStorage.setItem("User", JSON.stringify(logDeets));
    };
  
    const handleUserNameChange = e => {
      e.persist();
      const target = e.target;
      const value = target.value;
      const name = target.name;
      setUserInput(userInput => ({ ...userInput, [name]: value }));
      console.log(userInput);
    };
    return (
      <Container>
      <Form onSubmit={e => loginHandler(e)}>
      <Header>Instagram</Header>
        <FormInput
          placeholder="Phone number, username or email"
          name="username"
          type="text"
          value={userInput.username}
          onChange={handleUserNameChange}
        />
        <FormInput
          placeholder="Password"
          name="password"
          type="password"
          value={userInput.password}
          onChange={handleUserNameChange}
        />
        <SubmitBtn type="submit" value="Log In" />
      </Form>
      </Container>
    );
  };

export default Login;
```
`Notice how we passed in an object as the useState() argument and how we destructured the state in the setUserInput() function`

To add some authentication functionality we will need to create a HOC (higher order component).
Higher Order Components are components that receive components as parameters and returns the component with additional data and functionality. They are pure functions with zero side effects. HOC, as used in this project, is to manage our component render.

We'll start by creating a js file in our `authentication` folder and another in our `PostContainer` component

```
touch src/component/PostContainer/PostPage.js src/component/authentication/Authenticate.js
```

Now we'll do some code refactoring. In our App.js file, we'll cut out the SearchBar component and PostContainer component and paste it into our PostPage.js file.

PostPage.js

```javascript
import React from 'react';
import SearchBar from "../SearchBar/SearchBar";
import PostContainer from './PostContainer';


const PostPage = ({
    handleSearch,
    search,
    posts
}) => {
    return (
        <div>
            <SearchBar search={search} handleSearch={handleSearch} />
      {posts.map((userPost, index) => {
        return <PostContainer 
        key={index} 
        props={userPost} 

        />;
      })}
        </div>
    );
}

export default PostPage;
```

Then our App.js file
```javascript

  return (
    <AppContainer>
    <ComponentFromWithAuthenticate
        handleSearch={handleSearch}
        search={search}
        posts={posts}
      />
    </AppContainer>
  );

export default App;
```

Then in our Authenticate.js file, we input the following
```javascript
import React from 'react';

const Authenticate = (WrappedComponent, Login)  => class extends React.Component {
    render() {
      let viewComponent;
      if (localStorage.getItem("User")) {
        viewComponent = <WrappedComponent {...this.props}/>
      } else {
        viewComponent = <Login />
      }
      return (
        <div className="container">
          {viewComponent}
        </div>
      )
    }
  }

  export default Authenticate; 

```

And this concludes our mini-project.

Although we only used the `useState` and `useEffect` hooks (which are the basic and most widely used hooks) you can read about other react hooks and their uses in the react documentation.

## Project Links
The link to the complete project repo and hosted app can be found below:

[Instagram-clone](https://github.com/PascalUlor/Instagram-clone)

[Instagram-clone-netlify](https://5cfd8594d7a4c562852fee2e--admiring-shaw-fdc467.netlify.com/)

## Resources
[React Documentation](https://reactjs.org/docs/hooks-intro.html)
[Tom Bowden](https://dev.to/exodevhub/react-hooks-making-it-easier-to-compose-reuse-and-share-react-code-5he9)
[James King](https://upmostly.com/tutorials/using-custom-react-hooks-simplify-forms/)

![](https://thepracticaldev.s3.amazonaws.com/i/4spr6zi6sdrtwztb9p2n.gif)