React Tutorial – Learn React in 5 hours – Part 3

React functional component
Share

As we already discussed briefly about function component in the previous section of React Tutorial . Now we are discussing in detail about Function component.

With the addition of Hooks, React’s Function components are almost as powerful as its Class components, with the difference that you probably won’t need to use a Class component.

Class components are not being removed from React at the moment, even though Function components are preferred.

 

Create a Function Component

Class components return HTML and behave just like functional components, but function components require much less code and are easier to understand.

Example:
Create a Function component called Bike

function Bike() {
  return <h2>Hi, I am a Bike!</h2>;
}
The Bike component in your React application returns a <h2> element. To use this component in your React application, use the following syntax: <Bike />
Example:
Display the Bike component in the "root" element:

ReactDOM.render(<Bike />, document.getElementById('root'));
Output:
Create a function component

Props

You can pass components as props, which stands for properties. 

You send props into a component as attributes, much like function arguments.

In the previous chapter, you learned about props.

Example:
Use an attribute to pass a color to the Bike component, and use it in the render() function:

function Bike(props) {
  return <h2>I am a {props.color} Bike!</h2>;
}

ReactDOM.render(<Bike color="black"/>, document.getElementById('root'));
Output:
Props

Components in Components

Components within other components are described as follows:
Example:
Use the Bike component inside the Garage component:

function Bike() {
  return <h2>I am a Bike!</h2>;
}

function Garage() {
  return (
    <>
      <h1>Who lives in my Garage?</h1>
      <Bike />
    </>
  );
}

ReactDOM.render(<Garage />, document.getElementById('root'));
Output:
Component in component

Components in Files

The goal of React is to reuse code, so it is recommended to divide your components into separate files.

To do that, create a new .js file and paste the code inside:

It is important that the filename begins with a capital letter.

Example:
This is the new file, we named it "Bike.js":

function Bike() {
  return <h2>Hi, I am a Bike!</h2>;
}

export default Bike;
You have to import the file into your application in order to use the Bike component.
Example:
After importing the "Bike.js" file, we can use the Bike component as though it were created here.

import React from 'react';
import ReactDOM from 'react-dom';
import Bike from './Bike.js';

ReactDOM.render(<Bike />, document.getElementById('root'));
Output:
Component in files

React Hooks

Hooks were added to React in version 16.8.

While hooks generally replace class components, React does not plan to remove classes.

What is a Hook?

Hooks allow us to access React features such as state and lifecycle methods.

Example:
The following is an example of a Hook.If it does not make sense, don't worry about it.  We'll explain it more in this section..

import React, { useState } from "react";
import ReactDOM from "react-dom";

function FavoriteColor() {
  const [color, setColor] = useState("yellow");
  return (
    <>
      <h1>My favorite color is {color}!</h1>
      <button
        type="button"
        onClick={() => setColor("blue")}
      >Blue</button>
      <button
        type="button"
        onClick={() => setColor("yellow")}
      >Yellow</button>
      <button
        type="button"
        onClick={() => setColor("black")}
      >Black</button>
      <button
        type="button"
        onClick={() => setColor("purple")}
      >Purple</button>
    </>
  );
}

ReactDOM.render(<FavoriteColor />, document.getElementById('root'));
Output:
What is Hooks

From React, you must import Hooks.

To keep track of application state, we are using the useState Hook.

Generally, state refers to application data or properties that need to be tracked.

Hook Rules

There are 3 rules for hooks:

  • Hooks can only be called inside React function components.
  • Hooks can only be called at the top level of a component.
  • Hooks cannot be conditional
Note: React class components do not support hooks.

 

React useState Hook

UseState hooks in React allow us to track state in a function component.

Generally, a state refers to data or properties that an application needs to track.

Import useState

We first need to import the useState Hook into our component.

Example:
At the top of your component, import the useState Hook.

import { useState } from "react";

It is important to note that we are destroying useState from react since it is a named export.

Initialize useState

Using useState in our function component, we initialize our state.

The useState function returns two values based on the initial state:

  • The current state.
  • A function that updates the state.
Example:
The function component should be initialized at the top.

import { useState } from "react";

function FavoriteColor() {
  const [color, setColor] = useState("");
}
Again, the values returned from useState are being destructed.Our current state is represented by the first value, color.Using setColor as the second value, we can update our state.

Variables can be named anything you like.
Lastly, we set the initial state to an empty string: useState(“”)

Read State

In our component, we can now include our state anywhere.
Example:
In the rendered component, use the state variable.

import { useState } from "react";
import ReactDOM from "react-dom";

function FavoriteColor() {
  const [color, setColor] = useState("yellow");
  return <h1>My favorite color is {color}!</h1>
}

ReactDOM.render(<FavoriteColor />, document.getElementById('root'));
Output:
Read state

Update State

We update our state using our state updater function.

We should never directly update state. Ex: color = “yellow” is not allowed.

Example:
Use a button to update the state:

import { useState } from "react";
import ReactDOM from "react-dom";

function FavoriteColor() {
  const [color, setColor] = useState("yellow");
  return (
    <>
      <h1>My favorite color is {color}!</h1>
      <button
        type="button"
        onClick={() => setColor("red")}
      >Red</button>
    </>
  )
}

ReactDOM.render(<FavoriteColor />, document.getElementById('root'));
Output:
Update state

What Can State Hold

Use the useState Hook to keep track of strings, numbers, booleans, arrays, objects, and any combination of these!

To track individual values, we could create multiple state hooks.

Example:
Create multiple state Hooks:

import { useState } from "react";
import ReactDOM from "react-dom";

function Bike() {
  const [brand, setBrand] = useState("BMW");
  const [model, setModel] = useState("BMW R nineT");
  const [year, setYear] = useState("1960");
  const [color, setColor] = useState("black");

  return (
    <>
      <h1>My {brand}</h1>
      <p>
        It is a {color} {model} from {year}.
      </p>
    </>
  )
}

ReactDOM.render(<Bike />, document.getElementById('root'));
Output:
What can state hold
We can also use an object instead of a state!
Example:
Create a single Hook that holds an object:

import { useState } from "react";
import ReactDOM from "react-dom";

function Bike() {
  const [bike, setBike] = useState({
    brand: "BMW",
    model: "BMW R nineT",
    year: "1960",
    color: "black"
  });

  return (
    <>
      <h1>My {bike.brand}</h1>
      <p>
        It is a {bike.color} {bike.model} from {bike.year}.
      </p>
    </>
  )
}

ReactDOM.render(<Bike />, document.getElementById('root'));
Output:
What can state hold

As a result, since we are tracking one object, we must reference that object and then the property of that object in order to render the component. (Ex: bike.brand)

Updating Objects and Arrays in State

When state is updated, the entire state is overwritten.

Would it be possible to update our bike’s color only?

The setBike([color: “blue”]) function would remove the bike model, year, and brand from our state.

JavaScript’s spread operator can be used to help us.

Example:
Use the JavaScript spread operator to update only the color of the bike:

import { useState } from "react";
import ReactDOM from "react-dom";

function Bike() {
  const [bike, setBike] = useState({
    brand: "BMW",
    model: "BMW R nineT",
    year: "1960",
    color: "black"
  });

  const updateColor = () => {
    setBike(previousState => {
      return { ...previousState, color: "blue" }
    });
  }

  return (
    <>
      <h1>My {bike.brand}</h1>
      <p>
        It is a {bike.color} {bike.model} from {bike.year}.
      </p>
      <button
        type="button"
        onClick={updateColor}
      >Blue</button>
    </>
  )
}

ReactDOM.render(<Bike />, document.getElementById('root'));
Output:
Updating object array in state

In order to get the current value of state, we pass a function to our setBike function. The function receives the previous value.

In the next step, we return an object that spreads previousState and overwrites only the color.

React useEffect Hooks

With the useEffect Hook, you can perform side effects on your components.

As an example, you can fetch data, update the DOM directly, or use timers.

The useEffect function accepts two arguments. A second argument is optional.

useEffect(, )

Let’s use a timer as an example.

Example:
Use setTimeout() to count 2 second after initial render:

import { useState, useEffect } from "react";
import ReactDOM from "react-dom";

function Timer() {
  const [count, setCount] = useState(0);
  useEffect(() => {
    setTimeout(() => {
      setCount((count) => count + 2);
    }, 1000);
  });

  return <h1>I've rendered {count} times!</h1>;
}

ReactDOM.render(<Timer />, document.getElementById('root'));
Output:
React useEffect hook

However, hold on! I keeps counting even though it should only count once!

useEffect runs on every render. Therefore, when the count changes, a render occurs, causing another effect to take place.

This is not what we want. Several methods can be used to control side effects.

We should always include the second parameter which accepts an array. We can optionally pass dependencies to useEffect in this array.

1. No dependency passed:

useEffect(() => {
 //Runs on every render
});
2. An empty array:

useEffect(() => {
 //Runs only on the first render
}, []);
3. Props or state values:

useEffect(() => {
 //Runs on the first render
 //And any time any dependency value changes
}, [prop, state]);

As a result, let’s run this effect only on the initial render to fix this issue.

Example:
Only run the effect on the initial render:

import { useState, useEffect } from "react";
import ReactDOM from "react-dom";

function Timer() {
  const [count, setCount] = useState(0);
  useEffect(() => {
    setTimeout(() => {
      setCount((count) => count + 1);
    }, 1000);
  }, []); // <- add empty brackets here

  return <h1>I've rendered {count} times!</h1>;
}

ReactDOM.render(<Timer />, document.getElementById('root'));
Output:
React useEffect hook
Example:
The following is an example of a useEffect Hook that is dependent on a variable. If the count variable changes, the effect will run again:

import { useState, useEffect } from "react";
import ReactDOM from "react-dom";

function Counter() {
  const [count, setCount] = useState(0);
  const [calculation, setCalculation] = useState(0);

  useEffect(() => {
    setCalculation(() => count * 2);
  }, [count]); // <- add the count variable here

  return (
    <>
      <p>Count: {count}</p>
      <button onClick={() => setCount((c) => c + 1)}>+</button>
      <p>Calculation: {calculation}</p>
    </>
  );
}

ReactDOM.render(<Counter />, document.getElementById('root'));
Output:
React useEffect hook

Multiple dependencies should be included in the useEffect dependency array.

Effect Cleanup

It may be necessary to clean up some effects to prevent memory leaks.

A timeout, subscription, event listener, and other effects that are no longer used should be disposed.

To do this, we include a return function at the end of the useEffect Hook.

Example:
Clean up the timer at the end of the useEffect Hook:

import { useState, useEffect } from "react";
import ReactDOM from "react-dom";

function Timer() {
  const [count, setCount] = useState(0);
  useEffect(() => {
    let timer = setTimeout(() => {
    setCount((count) => count + 1);
  }, 1000);

  return () => clearTimeout(timer)
  }, []);

  return <h1>I've rendered {count} times!</h1>;
}

ReactDOM.render(<Timer />, document.getElementById("root"));
Output:
Effect clean up

Note: In order to clear the timer, we had to name it.

React useContext Hook

React Context

Context is a way to manage state globally in React.

Using it in conjunction with the useState Hook will make it easier to share state between deeply nested components than using useState alone.

The Problem

Stack components that require state access should be placed in the state holding component of the highest level of the stack.

Here is an example of many nested components. The components at the top and bottom of the stack need access to state.

In order to accomplish this without Context, we will need to pass the state as “props” through each nested component. This is known as “prop drilling”.

Example:
Passing "props" through nested components:

import { useState } from "react";
import ReactDOM from "react-dom";

function Component1() {
  const [user, setUser] = useState("johnny depp");

return (
    <>
      <h1>{`Hello ${user}!`}</h1>
      <Component2 user={user} />
    </>
  );
}

function Component2({ user }) {
  return (
    <>
      <h1>Component 2</h1>
      <Component3 user={user} />
    </>
  );
}

function Component3({ user }) {
  return (
    <>
      <h1>Component 3</h1>
      <Component4 user={user} />
    </>
  );
}

function Component4({ user }) {
  return (
    <>
      <h1>Component 4</h1>
      <Component5 user={user} />
    </>
  );
}

function Component5({ user }) {
  return (
    <>
      <h1>Component 5</h1>
      <h2>{`Hello ${user} again!`}</h2>
    </>
  );
}

ReactDOM.render(<Component1 />, document.getElementById("root"));
Output:
The Problem

Despite the fact that components 2-4 didn’t require the state, the state had to be passed along to component 5 in order to reach it.

The Solution

Context is the key to solving this problem.

Create Context

To create context, you should import and initialize createContext:

import { useState, createContext } from "react";
import ReactDOM from "react-dom";

const UserContext = createContext()

Next, we’ll wrap the tree of components that require the state context with the Context Provider.

Context Provider

Wrap child components in the Context Provider, and provide the state value.

function Component1() {

  const [user, setUser] = useState("johnny depp");

  return (
    <UserContext.Provider value={user}>
      <h1>{`Hello ${user}!`}</h1>
      <Component2 user={user} />
    </UserContext.Provider>
  );
}

All components in this tree will now have access to the user context.

Use the useContext Hook

In order to use the Context in a child component, we must use the useContext hook.

As a first step, import the useContext:

import { useState, createContext, useContext } from "react";
Then you can access the user Context in all components:
function Component5() {

  const user = useContext(UserContext);
  return (
    <>
      <h1>Component 5</h1>
      <h2>{`Hello ${user} again!`}</h2>
    </>
  );
}

Full Example

Example:
Here is the full example using React Context:

import { useState, createContext, useContext } from "react";
import ReactDOM from "react-dom";

const UserContext = createContext();

function Component1() {
  const [user, setUser] = useState("johnny depp");
  return (
    <UserContext.Provider value={user}>
      <h1>{`Hello ${user}!`}</h1>
      <Component2 user={user} />
    </UserContext.Provider>
  );
}

function Component2() {
  return (
    <>
      <h1>Component 2</h1>
      <Component3 />
    </>
  );
}

function Component3() {
  return (
    <>
      <h1>Component 3</h1>
      <Component4 />
    </>
  );
}

function Component4() {
  return (
    <>
      <h1>Component 4</h1>
      <Component5 />
    </>
  );
}

function Component5() {
  const user = useContext(UserContext);

  return (
    <>
      <h1>Component 5</h1>
      <h2>{`Hello ${user} again!`}</h2>
    </>
  );
}

ReactDOM.render(<Component1 />, document.getElementById("root"));
Output:
The Solution

In the next section, you will learn about remaining hooks.

Share

Leave a Comment

Your email address will not be published. Required fields are marked *

Share