marchello
marchello

Reputation: 2136

React Hooks useCallback & memo list re-rendering

I'm new to Hooks and trying to create a PureComponent equivalent version with Hooks. My goal is to create a multiselectable list with a child component that is reponsible for rendering the list items:

const Movie: FunctionComponent<{
  title: string,
  score: number,
  id: number,
  isSelected: boolean,
  setSelected: React.Dispatch<React.SetStateAction<{}>>
  }> = React.memo(({ title, score, isSelected, setSelected, id }) => {

  const selectMovie = (): void => {
    if (isSelected === null)
      return;
    setSelected(id);
  };

  const selected = {
    backgroundColor: "blue"
  }

  console.log("render movie")

  return (
    <div onClick={selectMovie} style={isSelected ? selected : {}}>
      {title}, {score}
    </div>
  )
})

The parent component have the data as well as the logic for the selection:

const App: FunctionComponent = () => {
  const data = [
    {
      id: 1,
      title: "Pulp fiction",
      score: 9
    },
    {
      id: 2,
      title: "Heat",
      score: 8
    },
    {
      id: 3,
      title: "American pie",
      score: 7
    }
  ]
  const [selectedItems, setSelected] = React.useState<{}>({});

  const selectMovie = React.useCallback((id: any) => {

    const sel: any = {...selectedItems};

    if (sel.hasOwnProperty(id)) {
      delete sel[id]
    } else {
      sel[id] = true;
    }

    setSelected(sel);
  }, [selectedItems])

  return (
    <div>
      {
        data.map(e => <Movie key={e.id} {...e} setSelected={selectMovie} isSelected={selectedItems.hasOwnProperty(e.id)}/>)
      }
    </div>
  )
}

I made a sandbox where you can try it out: https://codesandbox.io/s/old-sun-38n4u

The selection state is maintained in the parent component in an object and supplied to the child as a boolean. The problem is when I click on a movie all 3 list items re-renders (you can see the log in the console). I have used React.memo and useCallback as well to avoid arrow function re-creation for the props comparison. I'm pretty new to hooks, so it must be something silly I'm overlooking...

Upvotes: 2

Views: 832

Answers (1)

Tubc
Tubc

Reputation: 416

That is because your selectMovie is changing every time due to selectedItems dependency changing.

setSelected function can also take a function and you can get the selectedItems value so you don't need to set it as a dependency

Here is the working sandbox

Upvotes: 2

Related Questions