Sergio Xalambrí
June 15, 2019

# Introduction to React with the State & Effects hooks

Learn how to start a React project, create your first component and use the State and Effects hooks to implement basic behaviors.

## Starting a React Project

Let's start by creating a new React application, we could use the Create React App (CRA) tool to generate a basic boilerplate without configuration.

    $ npx create-react-app my-app

The `npx` command comes bundled with `npm` and let us use a command from a npm package without installing it globally.

## Running Your Project

Now that we have the project created we could access our application directory and start the project, to do so run the following commands.

    $ cd my-app
    $ yarn start

## Rendering an Element with React

You will notice your project comes with some files already created, delete all files inside the `src` folder and create a new `index.js` file with the content below.

    // src/index.js
    import React from "react";
    import { render } from "react-dom";

    const $root = document.getElementById("root");
    render(<h1>Hello, World!</h1>, $root);

This will render `<h1>Hello, World!</h1>` to the DOM using React, we just rendered our first element.

## Creating a Component

Now let's create our first component.

    // src/index.js
    function HelloWorld() {
      return <h1>Hello, World!</h1>;
    }

    const $root = document.getElementById("root");
    render(<HelloWorld />, $root);

A component is a normal JavaScript function but the name should start with a capitalized letter and it must return some kind of HTML code. There are other valid values like arrays or null, but you usually want to return HTML.

> Note: This is not real HTML, is something called JSX and in build time is transformed to function calls, specifically to the `React.createElement` function.

## Running an Effect

This time we will run a side effect, in this case we will change the title of the page, the one you read on the tab of your browser. To do it we need to use `React.useEffect`.

    // src/index.js
    function HelloWorld() {
      React.useEffect(() => {
        document.title = "Hello, World!";
      });

      return <h1>Hello, World!</h1>;
    }

This useEffect function is called a hook, a function you can use to rely on React to do different things, in this case to run a side effect after the component is rendered in the screen.

The useEffect hook receives a function and execute it after each render of the component (if the component is updated it will run it again). In our function we are changing the `document.title` to the string `Hello, World!`.

## Handling Events

One thing you will always need to do is to listen to events happening on the application and react to them, events like clicks, changes, submits, scroll, etc. In React we do that using `onEventName` where `EventName` is the name of the event, e.g. `onClick`, `onChange`, `onSubmit`, `onMouseOver`, etc.

    // src/index.js
    function HelloWorld() {
      React.useEffect(() => {
        document.title = "Hello, World!";
      });

      function handleChange(event) {
        console.log(event.target.value);
      }

      return (
        <main>
          <h1>Hello, World!</h1>
          <input type="text" defaultValue="Hello, World!" onChange={handleChange} />
        </main>
      );
    }

We are now creating an input of type text with a default value `Hello, World!` and we will listen to the change event, when the input changes it will call our `handleChange` function and run the `console.log(event.target.value)`.

## Using State

But we don't usually want to only log the value, we want to keep it and use it elsewhere in our application, to do so we use another hook from React, this one is called `React.useState` and let us keep values in memory and change them when we need them, when a state changes the component is rendered again with the new value.

    // src/index.js
    function HelloWorld() {
      const [title, setTitle] = React.useState("HelloWorld");

      React.useEffect(() => {
        document.title = "HelloWorld";
      });

      function handleChange(event) {
        setTitle(event.target.value);
      }

      return (
        <main>
          <h1>HelloWorld</h1>
          <input type="text" value={title} onChange={handleChange} />
        </main>
      );
    }

We are creating a new state and destructuring the resulting array in two elements, the first one `title` is the state value, the second one `setTitle` is a function React provides us to change the value of the state, we need to call it with the new state value.

In our input we changed `defaultValue` to `value`, this force the input to have our `title` state as value, that means it doesn't matter if the user writes something as long as the state doesn't change the input value will not change.

Here is where our `handleChange` works, it will read the new supposed value from the change event and pass it to `setTitle` to update the state, this will trigger a new render and update the input with the new value.

## Using State & Effect together

Using the state only to keep track of the value of an input is ok but it's not something really useful, let's synchronize the state of the input with the title of the document. We can use our `title` state inside our `useEffect` hook and change the title of the document dynamically based on what the user wrote in the input.

    // src/index.js
    function HelloWorld() {
      const [title, setTitle] = React.useState("HelloWorld");

      React.useEffect(() => {
        document.title = title;
      });

      function handleChange(event) {
        setTitle(event.target.value);
      }

      return (
        <main>
          <h1>{title}</h1>
          <input type="text" value={title} onChange={handleChange} />
        </main>
      );
    }

We could also use the value of the `title` state inside the `<h1>` to update it while the user is writing.

## Adding a Second State & Effect

Now let's add a second state and effect, inside our component we could have as many states and effects as we want/need, the only rule is they can't be inside an condition or loop. Let's keep track if the user is currently writing, like Slack or Facebook does in their chats.

    // src/index.js
    function HelloWorld() {
      const [title, setTitle] = React.useState("Hello, World!");
      const [isWriting, setIsWriting] = React.useState(false);

      React.useEffect(() => {
        if (!isWriting) {
          document.title = title;
        }
      });

      React.useEffect(() => {
        setTimeout(() => setIsWriting(false), 1000);
      });

      function handleChange(event) {
        setIsWriting(true);
        setTitle(event.target.value);
      }

      return (
        <main>
          <h1>{title}</h1>
          <input type="text" value={title} onChange={handleChange} />
          User is writing: {isWriting.toString()}
        </main>
      );
    }

We created a new state using `React.useState` and defaulted its value to `false`, the state we call it `isWriting` and the function to change it `setIsWriting`. We updated the original effect to only update the title of the document while the user is not writing.

Now we run a second effect where we are doing a `setTimeout` to update the `isWriting` state to false after a second. In the `handleChange` function we are changing both state, the `isWriting` to `true` and the `title` to the new content the user wrote.

At the end we added a single line to show in the UI if the user is writing, the `.toString()` is required to show the `true` or `false` as content.

## Adding an Effect Dependencies Array

If we run the example above it's possible to see before first second it's working fine and then it starts to update the state without waiting for the user to stop writing. This is because both effects are running after each render.

We could pass a second argument to `useEffect` which is an array listing the values from outside the effect our effect depends on. In our case the first effect will use `isWriting` and `title` from state, that means it depends on the values of those states, while the second one depends only in the `isWriting`.

The idea of this array of dependencies is we could limit our effect to only run if those dependencies changed. If `isWriting` didn't changed the second effect will not run, if `title` didn't changed too then even the first effect will not run.

    // src/index.js
    function HelloWorld() {
      const [title, setTitle] = React.useState("Hello, World!");
      const [isWriting, setIsWriting] = React.useState(false);

      React.useEffect(() => {
        if (!isWriting) {
          document.title = title;
        }
      }, [isWriting, title]);

      React.useEffect(() => {
        setTimeout(() => setIsWriting(false), 1000);
      }, [isWriting]);

      function handleChange(event) {
        setIsWriting(true);
        setTitle(event.target.value);
      }

      return (
        <main>
          <h1>{title}</h1>
          <input type="text" value={title} onChange={handleChange} />
          User is writing: {isWriting.toString()}
        </main>
      );
    }

## Clearing an Effect

This is working a little bit better, but still we are seeing the title of the document change after one second. What we can do now is clear the timeout between each call of our effect.

Inside an effect it's possible to return a function which will be executed before the next run of that effect, this let us clear the results of the previously run effect. In our case we could use it to run `clearTimeout`.

    // src/index.js
    function HelloWorld() {
      const [title, setTitle] = React.useState("Hello, World!");
      const [isWriting, setIsWriting] = React.useState(false);

      React.useEffect(() => {
        if (!isWriting) {
          document.title = title;
        }
      }, [isWriting, title]);

      React.useEffect(() => {
        const timer = setTimeout(() => setIsWriting(false), 1000);
        return () => clearTimeout(timer);
      }, [isWriting]);

      function handleChange(event) {
        setIsWriting(true);
        setTitle(event.target.value);
      }

      return (
        <main>
          <h1>{title}</h1>
          <input type="text" value={title} onChange={handleChange} />
          User is writing: {isWriting.toString()}
        </main>
      );
    }

## Lifting State Up

So far we created a single component, if we keep adding functionality to that component it will start to grow until it's hard, if not impossible, to maintain and add new features.

We could avoid that splitting it in different components and compose them in a parent component.

    // src/title.js
    import React from "react";

    function Title({ value, isWriting }) {
      React.useEffect(() => {
        if (!isWriting) {
          document.title = value;
        }
      }, [isWriting, value]);

      return <h1>{value}</h1>;
    }

    export default Title;

In our first component we move the `<h1>` and the effect to update the document's title to another component called `Title`. Our component will receive an object as first argument, this is called `props` and we can destructure it to read their properties, in our case `value` and `isWriting`.

    // src/input.js
    import React from "react";

    function Input({ value, onWrite }) {
      React.useEffect(() => {
        const timer = setTimeout(() => onWrite(value), 1000);
        return () => clearTimeout(timer);
      }, [value, onWrite]);

      function handleChange(event) {
        onWrite(event.target.value);
      }

      return <input type="text" value={value} onChange={handleChange} />;
    }

    export default Input;

In our second component we move the `<input />`, the `handleChange` and the effect to set if it's writing to another component called `Input`. This will receive two values inside our `prop`, the `value` of the input, the same we receive in `Title`, and a function to change the value called `onWrite`.

We will call this function with `event.target.value` to update it when the user writes something and inside our effect after one second with the same value, this change will make sense in the next component.

    // src/hello-world.js
    import React from "react";

    import Title from "./title";
    import Input from "./input";

    function HelloWorld() {
      const [title, setTitle] = React.useState("Hello, World!");
      const [isWriting, setIsWriting] = React.useState(false);

      function handleWrite(value) {
        setIsWriting(value !== title);
        setTitle(value);
      }

      return (
        <main>
          <Title value={title} isWriting={isWriting} />
          <Input value={title} onWrite={handleWrite} />
          User is writing: {isWriting.toString()}
        </main>
      );
    }

    export default HelloWorld;

Our latest component is our `HelloWorld`, this will import the `Title` and `Input` components and use them inside its return value sending `value`, `isWriting` and `onWrite` as props.

This component will also keep the states for `title` and `isWriting`, this is called "lift the state up", in our example those state are used inside our other component and our `HelloWorld` component too, because of this we can't move the value directly to the input since the data flow in React is single way from the top to the bottom of the component tree, we need to keep the state as near the top as required to be able to share the value, in our case that is `HelloWorld`.

Inside the `handleWrite` function we will update the value of `title` with the new received value and we will change `isWriting` to the result of the condition `value !== title`, this means if the value we received is the same as the current value we will set `isWriting` to false, if they are different we will set it to `true`.

With this we only need to render the `HelloWorld` component.

    // src/index.js
    import React from "react";
    import { render } from "react-dom";

    import HelloWorld from "./hello-world";

    const $root = document.getElementById("root");
    render(<HelloWorld />, $root);

###### Read next: Working with conditions & list in React

Learn how to conditionally render elements and how to properly work with lists in React.