useImperativeHandle

In notebook:
FrontEndMasters Intermediate React
Created at:
2019-06-23
Updated:
2019-08-15
Tags:
React libraries JavaScript

https://frontendmasters.com/courses/intermediate-react-v2/useimperativehandle/

useImperativeHandle

This is used very rarely, mostly for library authors.

It's used the reverse the data flow, so instead of moving from child to child, you go the other way. This let's a child control its parent.

The example below is for a form element.

   //****useImperativeHandle.js****

import React, {
  useState,
  useRef,
  useImperativeHandle,
  forwardRef
} from "react";

 // **** 1. we want to focus an input element.  ↴
 // which would be done by the parent
 // **** 4. exposed by forwardRef.  ↴
  // if a component calls `ref` property
  // it's going to make this function available
const ElaborateInput = forwardRef(
  ({ hasError, placeholder, value, update }, ref) => {
    const inputRef = useRef();
     // **** 2. exposing the function that the parent can run.  ↴
    useImperativeHandle(ref, () => {
      return {
       // **** 3. so the parent can "focus".  ↴
       // on the ElaborateInput 
        focus() {
          inputRef.current.focus();
        }
      };
    });
    return (
      <input
        ref={inputRef}
        value={value}
        onChange={e => update(e.target.value)}
        placeholder={placeholder}
        style={{
          padding: "5px 15px",
          borderWidth: "3px",
          borderStyle: "solid",
          borderColor: hasError ? "crimson" : "#999",
          borderRadius: "5px",
          margin: "0 10px",
          textAlign: "center"
        }}
      />
    );
  }
);

const ImperativeHandleComponent = () => {
  const [city, setCity] = useState("Seattle");
  const [state, setState] = useState("WA");
  const [error, setError] = useState("");
  const cityEl = useRef();
  const stateEl = useRef();

  function validate() {
    // lol I found it on StackOverflow : https://stackoverflow.com/a/25677072
    if (
      !/^([a-zA-Z\u0080-\u024F]+(?:. |-| |'))*[a-zA-Z\u0080-\u024F]+$/.test(
        city
      )
    ) {
      setError("city");
     // **** 5. here we can use the focus.  ↴
      cityEl.current.focus();
      return;
    }

    if (!/^[A-Z]{2}$/.test(state)) {
      setError("state");
      stateEl.current.focus();
      return;
    }

    setError("");
    alert("valid form!");
  }

  return (
    <div>
      <h1>useImperativeHandle Example</h1>
      <ElaborateInput
        hasError={error === "city"}
        placeholder={"City"}
        value={city}
        update={setCity}
        ref={cityEl}
      />
      <ElaborateInput
        hasError={error === "state"}
        placeholder={"State"}
        value={state}
        update={setState}
        ref={stateEl}
      />
      <button onClick={validate}>Validate Form</button>
    </div>
  );
};

export default ImperativeHandleComponent;

This technique is mostly reserved for library authors

You won't be writing much of these yourself.

We finished all the hooks!

This was the last hooks, now we've learned all the existing onces!