uber
uber

Reputation: 5073

How to deal with UseEffect running multiple times due to retrieval from Firestore DB?

edit: changed pretty much the whole body of this question

I am building a booking app. Tons of rerenders are happening and, following @jpmarks advice, I have tried to find out which useEffect could be the culprit, and think might be the piece of code below, which runs 24 times.

I have also uploaded all of the relevant components and console warnings to this repo, assuming that other components which render this may be affecting it.

useEffect(
    () => {
      console.log('useEffect ServicEstabelecimento ran') //runs 24 times

      async function getOfferedServicesFromDB() {
        const approved = await approvedBusinessService.doc(businessId)
        const snapshotApproved = await approved.get()
        if (snapshotApproved.exists && snapshotApproved.data().offeredServices) {
          setOfferedServices(Object.values(snapshotApproved.data().offeredServices))
        } else {
          const pending = await businessPendingApprovalService.doc(businessId)
          const snapshotPending = await pending.get()
          if (snapshotPending.exists && snapshotPending.data().offeredServices)
            setOfferedServices(Object.values(snapshotPending.data().offeredServices))
        }
        return
      }
      getOfferedServicesFromDB()
    },
    [
      /* businessId, setOfferedServices */
    ],
  )
  //React Hook useEffect has missing dependencies: 
  //'businessId' and 'setOfferedServices'. 
  //Either include them or remove the dependency array
  //Tried adding them separately or both, nothing changes

What I am trying to do here is see which services a business offers, getting that info from the database. If the business has been accepted or not yet, there's a change in how that's dealt with.

Upvotes: 4

Views: 694

Answers (2)

gdh
gdh

Reputation: 13682

One possibility is that in Options.js you are rendering ServiceEstabelecimento multiple times using serviceList.map. The serviceList is set in the useEffect with serviceListService.collection().onSnapshot callback. With this, naturally your useEffect will be called 24 times(if serviceList gets a value of 24 in snapshot callback)

Another possibility is that you are rendering ServiceEstabelecimento with a ternary. This will unmount/re-mount the componentthe component based on the value of estabelecimento.

const ContextOptionsEstabelecimento = React.createContext()

export const Options = () => {
  const { estabelecimento, theme, offeredServices } = useContext(Context)
  const [isConfiguring, setIsConfiguring] = useState(false)
  const [serviceList, setServiceList] = useState([])

  useEffect(() => {
    const unsubscribeServices = serviceListService.collection().onSnapshot(snapshot => {
      const services = snapshot.docs.map(collectIdsAndDocs)
      setServiceList(services) //<-----see here
    })

    return () => {
      unsubscribeServices()
    }
  }, [])

  const serviceElements = serviceList.map(service => //<-----see here
    !estabelecimento ? ( //<-----see here
      <Service
        key={service.id}
        id={service.id}
        name={service.name}
        type={service.type}
        title={service.info}
        duration={service.duration}
      />
    ) : ( //<-----see here
      <ContextOptionsEstabelecimento.Provider value={{ isConfiguring }} key={service.id}>
        <ServiceEstabelecimento
          key={service.id}
          id={service.id}
          name={service.name}
          type={service.type}
          title={service.info}
          duration={service.duration} 
        />
      </ContextOptionsEstabelecimento.Provider>
    ),
  )

  return (
 ...

Make checks on the above and see how you go. Also share the complete repo(if possible) to debug further.

Also I see some architecting issues as I see large objects been used with multiple contexts. This will have risk of components re-rendering unnecessarily. I am assuming you are taking care of such kind of things.

Upvotes: 2

Jan-Philipp Marks
Jan-Philipp Marks

Reputation: 1539

I looked through your source code and it looks like the component is not responsible for the unneccessary renders. I recommend fixing the dependencies as your editor is telling you, see if that is fixing something. I strongly believe its your context that gets updated inside of one of your effects that causes the context to rerender the component. To find out what state from context is causing the renders you can use the useEffect on each of them to check.

const { theme, userObject, dateForRenderingTimeGrid } = useContext(Context);

useEffect(() => {
  console.log("Theme updated");
}, [theme]);
useEffect(() => {
  console.log("UserObject updated");
}, [userObject]);
useEffect(() => {
  console.log("DateForRenderingTimeGrid updated");
}, [dateForRenderingTimeGrid ]);

Let me know what you are seing. If none of these are actually triggering an effect but you still see the renders, then you can be certain that its happening in your component

Upvotes: 3

Related Questions