Using forwarRef and useImperativeHandle

77 Views Asked by At
import { forwardRef, useImperativeHandle, useRef, useState } from "react";
const items = [{ name: "john" }, { name: "mathew" }, { name: "rahul" }];
export default function App(){
  const [selectedPerson,setSelectedPerson] = useState({name:""});
  return (
        <>
     <p>
       Slected Person:{" "}
       {selectedPerson.name ? selectedPerson.name : "No one Selected"}
     </p>
      <ul>
        {items.map((item) => {
          return (
            <li
              onClick={() => {
                setSelectedPerson({name:item.name});
              }}
              key={item.name}
            >
              <button> {item.name}</button>
            </li>
          );
        })}
      </ul>
    </>
  )
}

Above code renders a list of buttons with names and a when any button is pressed the component state is changed and the selected Person's name will be displayed at the top of the list. The problem with the above code is that when the button is selected whole list of button is also re-rendered, which is unnecessary, only the top section that displays the selected name should be re-rendered. I dont' want to use useMemo,useCallback or wrapping a React FC in a React.memo. What i tried at first was to split the components and sending the list component as children component to the component with selected name element. I couldn't do it because the button components needs to have access to setSelectedName(). So the solution i came up with involves using forwardRef and useImperativeHandle which works fine. Following is the code for that.

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

const items = [{ name: "john" }, { name: "mathew" }, { name: "rahul" }];

type SelectedPersonHandle = {
  setName: (name: string) => void;
};

type SelectedPersonProps = {};

const SelectedPersonComponent = forwardRef<
  SelectedPersonHandle,
  SelectedPersonProps
>((props, ref) => {
  const [selectedPerson, setSelectedPerson] = useState({ name: "" });

  useImperativeHandle(ref, () => ({
    setName: (name: string) => {
      setSelectedPerson({ name });
    }
  }));

  return (
    <p>
      Slected Person:{" "}
      {selectedPerson.name ? selectedPerson.name : "No one Selected"}
    </p>
  );
});

export default function App() {
  const selectedPersonRef = useRef<SelectedPersonHandle>(null);

  console.log("App component rendering");
  return (
    <>
      <SelectedPersonComponent ref={selectedPersonRef} />
      <ul>
        {items.map((item) => {
          return (
            <li
              onClick={() => {
                selectedPersonRef.current?.setName(item.name);
              }}
              key={item.name}
            >
              <button> {item.name}</button>
            </li>
          );
        })}
      </ul>
    </>
  );
}

But many blogs i read says that you must use these features only as last resort. I need to know why they are saying that. is it because of performance loss or any other reason. and how does using the forwardRef and useImperativeHandle compare with using useMemo or React.memo in terms of performance and memory usage.Also if anyone has a better solution without using any of the hooks and by just passing components as props. Please share it

0

There are 0 best solutions below