React - Introduction to Hooks, useState

Until recently, functional components were considered as dumb/stateless/presentational components as they couldn't have and maintain their own state.

With the introduction of Hooks, it has changed. By introducing hooks, react has given a way forward to use functional components in the same capacity as class components as its widely believed that functional components improve performance of the application. 

Even though class components are great, it is generally believed that functional components are closest to React's design language. Classes require too much of boilerplate code, your code becomes lengthy even after its transpiled by Babel. But it has its own advantages too.

Whereas functional components as easy to read, write and require less boilerplate code as compared to class components.

Hooks provide us a way to hook-into the lifecycle methods of a react component in functional components, which was earlier limited to class components only. 

Hook is a function, which takes default value you want to set and returns an array. Note that react internally cannot keep track of hooks that run out of order, hence their calling order is important!

There are several hooks are available which we can use, such as:

  • useState
  • useReducer
  • useContext
  • useRef
  • useCallback
  • useMemo
  • useEffect

In this article, we will look into how we can make (former dumb/stateless/presentational) functional component more meaningful using useState hook.

    useState

      In this article, we will convert our existing ReactLifecycle class component to a stateful functional component which we have seen in update lifecycle method earlier.

      useState is a function, which returns an array. useState hook is available in react package and we need to import it as a named package from React. An alternative is to use React.useState if you dont want to import it directly.


          //useState returns an array of 2 items
          //first value is state and second is dispatchAction to modify that state
          //default value can be profived, otherwise its initialized to undefined
          //we can use primitive types, objects, arrays etc. with useState
          const [clickCount, setClickCount] = useState(0);

          console.log(useState()); //returns [undefined, Æ’]
          console.log(useState("Hardik")); //returns ['Hardik', Æ’]



      Every hook including useState returns an array of 2 elements by setting up default value which we pass to it and the values of the array can be retrieved as a normal array values.

      By default, it returns an array [undefined, dispatchAction] in case there is no default value provided. We can use any primitive type or objects, arrays etc. with useState.

      With hook in functional component, we dont have to use this object anymore with our state and methods. 

      Lets look at an example:

         
      import React, { useState } from "react";
          import ReactDOM from "react-dom";
          import "./styles.css";

          const ReactLifecycle = () => {

          //useState returns an array of 2 items
          //first value is state and second is dispatchAction to modify that state
          //default value can be profived, otherwise its initialized to undefined
          //we can use primitive types, objects, arrays etc. with useState
          const [clickCount, setClickCount] = useState(0);

          // console.log(useState()); //returns [undefined, Æ’]
          // console.log(useState("Hardik")); //returns ['Hardik', Æ’]

          const incrementHandler = (event, name) => {
              console.log("clickCount is incremented by One...");
              const newClickCount = clickCount + 1;
              setClickCount(newClickCount);
          };

          const resetHandler = () => {
              console.log("clickCount is reset to Zero..");
              const newClickCount = 0;
              setClickCount(newClickCount);
          };

          return (
              <>
              <h1>Hello, Total clicks are {clickCount}</h1>
              <button className="btn" onClick={(event) => incrementHandler()}>
                  Click Now to increment!
              </button>
              <button className="btn" onClick={(event) => resetHandler()}>
                  Reset Count!
              </button>
              </>
          );
          };

          const rootElement = document.getElementById("root");
          ReactDOM.render(<ReactLifecycle />, rootElement);



      Lets delve deeper into our code.

      We have retained most of our logic from the corresponding class component. Though there are some noticeable changes such as:

        • no need of this  keyword anymore to refer to state, methods etc.

        • With class components, we had only one this.setState method. But with useState, we can have setXXX method dedicated for every state object.

        • The name of the dispatch action can be set to anything, but Its a standard practice to name it as set followed by the state object name in pascal case.

          • ex., clickCount is state object, setClickCount is name of the dispatch action/function.

      Output:














              As can be seen in the above snapshot, the clickCount was incremented to 4 (refer console log with badge value 4) and on click of Reset Count button, its reset to zero.

              Using setState with objects

              As discussed earlier, setState can be initialized with any value. Lets try to set an object and on click on the button Click Now to Increment, change the message property to number of clicks.

              We will add a person object with some default values and default message. Whenever button to increment the clickCount is clicked for the first time, we will change the default message to "Hello from Hardik". We will also leverage string literals/string templates for this example.


                  import React, { useState } from "react";
                  import ReactDOM from "react-dom";
                  import "./styles.css";

                  const ReactLifecycle = () => {
                  //useState returns an array of 2 items
                  //first value is state and second is dispatchAction to modify that state
                  //default value can be profived, otherwise its initialized to undefined
                  //we can use primitive types, objects, arrays etc. with useState
                  const [clickCount, setClickCount] = useState(0);

                  //setState is initialized to a person object
                  const [person, setPerson] = useState({
                      id: 1,
                      name: "Hardik",
                      message: "Default Message",
                  });

                  // console.log(useState()); //returns [undefined, Æ’]
                  // console.log(useState("Hardik")); //returns ['Hardik', Æ’]

                  const incrementHandler = (event, name) => {
                      console.log("clickCount is incremented by One...");
                      const newClickCount = clickCount + 1;
                      setClickCount(newClickCount);

                      //change person.message using string literal
                      //notice that we are copying earlier object values directly using spread operator
                      //before updating the spefic property
                      setPerson({
                      ...person,
                      message: `Hello, from ${person.name}`,
                      });
                  };

                  const resetHandler = () => {
                      console.log("clickCount is reset to Zero..");
                      const newClickCount = 0;
                      setClickCount(newClickCount);
                  };

                  return (
                      <>
                      <h1>{person.message}</h1>
                      <button className="btn" onClick={(event) => incrementHandler()}>
                          Click Now to increment!
                      </button>
                      <button className="btn" onClick={(event) => resetHandler()}>
                          Reset Count!
                      </button>
                      </>
                  );
                  };

                  const rootElement = document.getElementById("root");
                  ReactDOM.render(<ReactLifecycle />, rootElement);


              Output:

              • Before Incrementing count


















              • After incrementing the count:



















              • Notice that we are copying earlier state object values directly using spread operator before updating the specific property to make sure we are not overriding other properties.
              • If we only update person.message property without above step, person state will only have person.message property and id, name will be overwritten:



              Comments

              Popular Posts