React Hooks by Example - Part 2
Tags: react, webdev, hooks, javascript
Published: 2023-02-11 14:15:52

## Learn (more) Hooks = Make (more) Money!🤑  

We're back to make more Dinero!💵💵💵
Let's explore some of the less commonly used, but nonetheless super-important hooks in react!

The code for all the examples can be found at this [Code Sandbox](https://codesandbox.io/s/u2tbtz).
This is Part 2 of a three-part series, and it covers **useReducer**, **useRef**, **useMemo**, **useCallback**, **useImperativeHandle** and **useLayoutEffect**!

---
## useReducer
- The `useReducer` can be used as an alternative to `useState`.
- `useReducer` is usually preferable to `useState` when you have complex state logic that involves multiple sub-values or when the next state depends on the previous one."
- Example:
    ```javascript
    // /src/UseReducerExample.js

    import { useReducer } from "react";
    import { Link } from "react-router-dom";

    const bankBalance = { counter: 0 };

    function reducer(state, action) {
      switch (action.type) {
        case "make 1 $":
          return { counter: state.counter + 1 };
        case "spend 1 $":
          return { counter: state.counter - 1 };
        default:
          throw new Error();
      }
    }

    export default function UseReducerExample() {
      const [state, dispatch] = useReducer(reducer, bankBalance);

      return (
        <>
          <h1>
            <Link to={"/"}>
              Dinero!
            </Link>
          </h1>
          <h2>
            {state.counter >= 0 ? "You have" : "You lost"} {Math.abs(state.counter)}{" "}
            💵
          </h2>

          <button onClick={() => dispatch({ type: "spend 1 $" })}>Spend 1 $</button>
          <button onClick={() => dispatch({ type: "make 1 $" })}>Make 1 $</button>
        </>
      );
    }
    ```
- In the above example:
  - The `useReducer` hook accepts a `reducer` function and an data which you want to save to state, *here, { counter: 0 }*.
  - The `useReducer` hook returns a `state` object containing this data, and a `dispatch` function.
  - When `dispatch` is called with an `action` *here, { type: "spend 1 $" }*,
    - It passes the `state` and this `action` to the `reducer` function.
    - Depending on the `action` type, it will return some value and this value will be saved to the `state` *here, state.counter*
  - The updated value of the state can then be accessed inside the component.
- See it in action [here](https://u2tbtz.csb.app/use-reducer) and the code at this [sandbox](https://codesandbox.io/s/react-hooks-examples-u2tbtz?file=/src/UseReducerExample.js)! 

---
## useRef
- The `useRef` hook can be used to reference a given value, and it will be stored in its `.current` property between component re-renders, and can be changed.
- A common use-case is to access DOM elements directly.
- It is important to note that mutating `.current` will not cause a re-render.
- Example:
    ```javascript
    // /src/UseRefExample.js
    
    import { useRef } from "react";
    import { Link } from "react-router-dom";

    export default function UseRefExample() {
      let counter = 0;
      const refAmt = useRef(null);

      const updateAmt = () => {
        console.log(refAmt.current);

        counter += 1;
        console.log(`You actually have ${counter} 💵`);
      };

      return (
        <>
          <h1>
            <Link to={"/"}>
              Dinero!
            </Link>
          </h1>
          <h2 ref={refAmt}>You have {counter} 💵</h2>
          <button onClick={updateAmt}>Get 1 $</button>
        </>
      );
    }
    ```
- In the above example:
  - We create a ref, `refAmt`.
  - in the `<h2>` element we specify `ref={refAmt}`, so now `refAmt.current` points to the `<h2>` element.
  - On clicking the button, we can open the console and see that,
    - The value of `counter` is increasing.
    - But `refAmt.current` (which points to the `<h2>` element) is not being updated with the new value of `counter`.
- See it in action [here](https://u2tbtz.csb.app/use-ref) and the code at this [sandbox](https://codesandbox.io/s/react-hooks-examples-u2tbtz?file=/src/UseRefExample.js)!

---
## useMemo
- the `useMemo` hook takes a function (that ideally returns something) and an array of dependencies.
- It will only re-compute the return value if one or more of the dependencies has changed.
- This helps to optimizae expensive computations on each render, that may not necessarily need to be re-computed.
- If no array is provided, a new value will be computed on every render.
- Every value referenced inside the function should also appear in the dependencies array.
- Side effects should be in `useEffect`, not `useMemo`.
- Example:
    ```javascript
    // /src/UseMemoExample.js

    import { useState, useMemo } from "react";
    import { Link } from "react-router-dom";

    function calculationRes(constant) {
      const result = constant ** 2;
      console.log(`The calculated result is: ${result}`);
      return result;
    }

    export default function UseMemoExample() {
      const [counter, setCounter] = useState(0);
      const [constant] = useState(1000000);
      const calculation = useMemo(() => calculationRes(constant), [constant]);

      return (
        <>
          <h1>
            <Link to={"/"}>
              Dinero!
            </Link>
          </h1>
          <h2>You have {counter} 💵</h2>
          <button onClick={() => setCounter(counter + 1)}>Get 1 $</button>
          {calculation}
        </>
      );
    }
    ```
- In the above example:
  - The function `calculationRes` runs once, the result is memoized and `result` is logged in console.
  - On clicking on the button, the state `counter` changes and hence the component re-renders.
  - But on subsequent renders, `result` is not logged to console.
  - This is because the value of `result` has not changed since the value of the variable `constant` has not changed, which was in the dependency array for `calculationRes`.
- See it in action [here](https://u2tbtz.csb.app/use-memo) and the code at this [sandbox](https://codesandbox.io/s/react-hooks-examples-u2tbtz?file=/src/UseMemoExample.js)!

---
## useCallback
- The `useCallback` hook is very similar to the `useMemo` hook, the only difference being that `useMemo` returns a memoized value, and `useCallback` returns a memoized function.
- A common use-case is when the function needs to be passed down nested child components.
- The example above can be referred to, as an example of `useCallback` by just replacing:  
  `const calculation = useMemo(() => calculationRes(constant), [constant]);` 
  with 
    ```javascript
    const calculation = useCallback(() => calculationRes(constant), [constant]);
    ```
- See it in action [here](https://u2tbtz.csb.app/use-callback) and the code at this [sandbox](https://codesandbox.io/s/react-hooks-examples-u2tbtz?file=/src/UseCallbackExample.js)!

---
## *forwardRef* and useImperativeHandle
- As we have seen above, the `useRef` hook can be used to access DOM elements.
- We can wrap the component exposing this `ref`, inside `forwardRef` and then the parent component would have access to this ref.
- Example of forwarding refs:
    ```javascript
    import { useRef, forwardRef } from "react";

    function ChildComponent(props, ref) {
      return (
        <>
          <h1 ref={ref} {...props}>{"💵💵💵💵💵💵💵💵💵💵"}</h1>
        </>
      );
    }

    ChildComponent = forwardRef(ChildComponent);

    export default function UseImperativeHandleExample() {
      const moneyRef = useRef(null);

      return (
        <>
          <ChildComponent ref={moneyRef} />
        </>
      );
    }
    ```
- In the above example:
  - The parent component `UseImperativeHandleExample` uses the `useRef` hook to create a ref object.
  - This ref is pointed to `ChildComponent`.
  - `ChildComponent` is wrapped inside `forwardRef` and hence we can assign this ref object to a DOM element inside it, here, the `<h1> element`.
  - Now, the parent component `UseImperativeHandleExample` will have access to the ref to the `<h1> element` inside `ChildComponent`.

- Now, if we need to modify the behavious of this ref, based on say, some user action, this is where the `useImperativeHandle` hook comes in handy!
- The above example can be modified like so:
    ```javascript
    // /src/UseImperativeHandleExample.js

    import { useRef, forwardRef, useImperativeHandle } from "react";
    import { Link } from "react-router-dom";

    function ChildComponent(props, ref) {
      const h1Ref = useRef(null);

      useImperativeHandle(ref, () => ({
        seeReferencedEl: h1Ref.current
      }));

      return (
        <>
          <h1>
            <Link to={"/"}>
              Dinero!
            </Link>
          </h1>
          <h1 ref={h1Ref} {...props}>
            {"💵💵💵💵💵💵💵💵💵💵"}
          </h1>
        </>
      );
    }

    ChildComponent = forwardRef(ChildComponent);

    export default function UseImperativeHandleExample() {
      const moneyRef = useRef(null);

      const seeReferencedElement = () => {
        console.log(moneyRef.current?.seeReferencedEl);
      };

      return (
        <>
          <ChildComponent ref={moneyRef} />
          <button onClick={seeReferencedElement}>See ref in console</button>
        </>
      );
    }
    ```
- In the above example:
  - Inside the `UseImperativeHandleExample` component, `moneyRef.current` points to `ChildComponent` as we saw in the *forwardRef* example previously.
  - But in this example, we assign a new ref object `h1Ref` to the `<h1> element` inside `ChildComponent`.
  - Using the `useImperativeHandle` hook, we assign this new `h1Ref` to the `seeReferencedEl` property of `ChildComponent`'s ref.
  - So now, inside the `UseImperativeHandleExample` component, `moneyRef.current.seeReferencedEl` points to the `<h1> element` inside `ChildComponent`.
- See it in action [here](https://u2tbtz.csb.app/use-imperative-handle) and the code at this [sandbox](https://codesandbox.io/s/react-hooks-examples-u2tbtz?file=/src/UseImperativeHandleExample.js)!

---
## useLayoutEffect
- The `useLayoutEffect` is similar to `useEffect`, except one key difference.
-  It runs (synchronously) after the elements have been loaded into the DOM but before they are painted to the UI.
- "Use this [hook] to read layout from the DOM and synchronously re-render".
- A common use-case is to measure layout and change the position of elements based on it.
- Example:
    ```javascript
    // /src/UseLayoutEffectExample.js
    import { useRef, useEffect, useLayoutEffect } from "react";
    import { Link } from "react-router-dom";

    export default function UseLayoutEffectExample() {
      const refAmt = useRef(null);

      useEffect(() => {
        console.log("useEffect called!");
      }, []);

      useLayoutEffect(() => {
        console.log("useLayoutEffect called");
      }, []);

      return (
        <>
          <h1>
            <Link to={"/"} style={styles.linkStyle}>
              Dinero!
            </Link>
          </h1>
          <h2 ref={refAmt}>You have 1000000 💵</h2>
        </>
      );
    }
    ```
- In the above example:
  - We see that `"useLayoutEffect called"` is logged first, since the `useLayoutEffect` runs synchronously 
  - Then the DOM is visually updated.
  - Then `"useEffect called!"` is logged, since `useEffect` runs asynchronously.
- See it in action [here](https://u2tbtz.csb.app/use-layout-effect) and the code at this [sandbox](https://codesandbox.io/s/react-hooks-examples-u2tbtz?file=/src/UseLayoutEffectExample.js)!  

Hope you had great fun learning hooks, and heading to the Buggati store soon for some Sunday shopping!
Stay tuned for Part 3 where we look at more hooks🤘
Cheers!