Joey Coder
Joey Coder

Reputation: 3499

Implementing useMemo the right way

I am trying to understand the usage of useMemo. Since the object doesn't change, I thought I can improve performance by adding useMemo. However as soon I add it here, I am being asked to add findFloorPosition. Once I did that my linter asks me to add useCallback to the findFloorPosition function. Can you give me some advice what's the right way to implement useMemo here?

const location = useLocation();
const searchParams = parseQs(location.search);
const goto = searchParams?.goto;

const findFloorPosition = (elementId) => {
  for (const floor of blueprint) {
    for (const room of floor.rooms) {
      const foundElement = room.elements.find(
        (element) => element.id === elementId
      );
      if (foundElement) {
        return floor.position;
      }
    }
  }
};

const currentFloorPosition = useMemo(() => findFloorPosition(goto), [goto]);

Probably not relevant here, but here how the blueprint object looks like:

const blueprint = [
  {
    id: "4mD59WO",
    name: "AUDITORIUM",
    position: 1,
    rooms: [
      {
        id: "zR8Qgpj",
        name: "Audimax",
        subtitle: null,
        details: null,
        position: 0,
        elements: [
          {
            id: "1jLv04W",
            position: 0,
            type: "daily",
            element: "listing_large",
            properties: {
              meetingId: null,
              capacity: 6
            }
          },
          {
            id: "1jLv12W",
            position: 1,
            type: "daily",
            element: "listing_large",
            properties: {
              meetingId: null,
              capacity: 6
            }
          }
        ]
      }
    ]
  },
  {
    id: "3mDd9WO",
    name: "FOYER",
    position: 0,
    rooms: [
      {
        id: "4R8Qgpj",
        name: "Speakers Table",
        subtitle: null,
        details: null,
        position: 0,
        elements: [
          {
            id: "2jLv04W",
            position: 0,
            type: "daily",
            element: "listing_large",
            properties: {
              meetingId: null,
              capacity: 6
            }
          },
          {
            id: "2jLv12W",
            position: 1,
            type: "daily",
            element: "listing_large",
            properties: {
              meetingId: null,
              capacity: 6
            }
          }
        ]
      }
    ]
  }
];

Upvotes: 0

Views: 415

Answers (2)

Shubham Khatri
Shubham Khatri

Reputation: 281744

Since functional components rely heavily on closures, its extremely important that when you memoize functions, you are using the correct and updated values from the closures.

The reason eslint warns you to add findFloorPosition to dependency is to make sure that nothing within findFloorPosition refers to old values

The above code can be implemented like

const findFloorPosition = useCallback((elementId) => {
  for (const floor of blueprint) {
    for (const room of floor.rooms) {
      const foundElement = room.elements.find(
        (element) => element.id === elementId
      );
      if (foundElement) {
        return floor.position;
      }
    }
  }
}, [blueprint]);

const currentFloorPosition = useMemo(() => findFloorPosition(goto), [goto, findFloorPosition]);

Upvotes: 1

Dennis Vash
Dennis Vash

Reputation: 53894

Notice that you should memoize a value only if the dependency array values (the value of goto) not frequently change.

The warning occurs because the linter (eslint) only evaluates the semantics, it doesn't know that findFloorPosition is just a helper function.

So basically you need to inline the helper function like:

const currentFloorPosition = useMemo(() => {
  for (const floor of blueprint) {
    for (const room of floor.rooms) {
      const foundElement = room.elements.find(
        (element) => element.id === goto
      );
      if (foundElement) {
        return floor.position;
      }
    }
  }
  return null;
}, [goto]);

// Or
const currentFloorPosition = useMemo(() => {
  const findFloorPosition = (elementId) => {
  for (const floor of blueprint) {
    for (const room of floor.rooms) {
      const foundElement = room.elements.find(
        (element) => element.id === elementId
      );
      if (foundElement) {
        return floor.position;
      }
    }
  }
};
  return findFloorPosition(goto);
}, [goto]);

Upvotes: 0

Related Questions