user1354934
user1354934

Reputation: 8851

How can I prevent compound component from re-rendering?

I am using react context, and all it contains at the moment are 3 items: contacts and editingContact, and editContact:

interface ContactsContextProps {
  contacts: Contact[];
  editingContact: Contact;
  editContact: (contact: Contact) => () => void // being lazy and this is from an onClick
}

const ContactsContext = React.createContext<Partial<ContactsContextProps>>({
  editContact: (contact: Contact) => () => {}
})

const ContactsProvider: React.FunctionComponent = props => {
  const [contacts, setContacts] = useState<Contact[]>();
  const [editingContact, setEditingContact] = useState<Contact>();

  React.useEffect(() => {
   // fetch contacts, and setContacts(contacts)
  }, [])

  const editContact = React.useCallback((contact: Contact) => {
    return function() {
      setEditingContact(contact);
    }
  })

  return (
    <ContactsContext.Provider
      value={{
        editingContact,
        editContact,
        contacts
      }}
    >
      {props.children}
    </ContactsContext.Provider>
  )
}

Here's how it is being used:

const ContactsList: React.FunctionComponent<{
  contacts: Contact[];
}> = React.memo(props => {
  return (
    <>
      {props.contacts.map(contact => (
        <Card key={contact.id} contact={contact} />
      ))}
    </>
  );
});

const Wrapper: React.FunctionComponent = () => {
  const contactsCtx = React.useContext(ContactsContext);

  return (
    <>
      <Box className={styles.main}>
        <Header />
        {contactsCtx.contacts && <ContactsList contacts={contactsCtx.contacts} />}
      </Box>
      {contactsCtx.editingContact && <EditContactModal />}
    </>
  );
};

The <Card /> only has an edit button right now, which calls contactsContext.editContact(). However, each time this is called, all the Cards re-render. I placed a console.log('card') in each Card, and it logs card 10 times (I have 10 contacts right now).

What am I doing wrong?

Upvotes: 3

Views: 387

Answers (1)

damjtoh
damjtoh

Reputation: 385

There has been a discussion in a React Github issue, basically there is 3 possible solutions for this:

  • Option 1 (Preferred): Split contexts that don't change together
  • Option 2: Split your component in two, put memo in between
  • Option 3: One component with useMemo inside

You should check the link for examples about it.

Upvotes: 1

Related Questions