1

Can you guys help me to solve why am i having a infinite re-render loop when I call a function of my Context that fetches API data? (Sorry about the mistakes and bad practices I may have used in my code, i'm a fresh starter on IT and Development.)

This is my Component:

export const LinkIdeasModal: React.FC<LinkIdeasProps> = ({
  idea,
  isOpen,
  onRequestClose,
}): JSX.Element => {
  const { colors } = useTheme();
  const { linkIdeas, unLinkIdeas, listLinkedIdeas, linkedIdeas } = useContext(ApprovalFunnelContext);
  const { getIdeasForLink, ideasForLink } = useContext(IdeaContext);
  const [isAddingIdea, setIsAddingIdea] = useState(false);
  const [isSearchingIdea, setIsSearchingIdea] = useState(false);
  const [searchState, setSearchState] = useState('');

  const handleListLinkedIdeas = useCallback(async (): Promise<void> => {
    await listLinkedIdeas(idea.id);
  }, [listLinkedIdeas, idea]) ;

  const handleLinkIdea = useCallback(async (ideaToLink: string) => {
    await linkIdeas(idea.id, ideaToLink);
    console.log(`idea primária: ${idea.id}`);
    console.log(`idea secundária: ${ideaToLink}`);
  }, [linkIdeas, idea]);

  const handleUnlinkidea = useCallback(async (secondaryIdeaId) => {
    await unLinkIdeas({
      params1: idea.id,
      params2: secondaryIdeaId,
    });
  }, [idea.id, unLinkIdeas])

  const handleClose = useCallback(() => {
    setIsAddingIdea(false);
    onRequestClose();
  }, [setIsAddingIdea, onRequestClose])

  const handleSearching = useCallback(() => {
    setIsSearchingIdea(true);

  }, [])

  const handleNotSearching = useCallback(() => {
    setIsSearchingIdea(false)
  }, [])

  const buildIdeaTitle = (name: string): string => {
    const maxWidth = 250;
    return getTextWidth(name) > maxWidth
      ? getStringWith3Dots(name, maxWidth)
      : name;
  };

  useEffect(() => {
    (async () => {
      await getIdeasForLink({
        search: searchState,
      });
      console.log(linkedIdeas)
    })()
    handleListLinkedIdeas()
  }, []);

  return (
    <Modal
      isOpen={isOpen}
      onRequestClose={handleClose}
      className="react-modal-idea-links"
      overlayClassName="react-modal-overlay"
    >
      <Container>
        <ModalHeader>
          <LinkTitleWrapper>
            <HiOutlineLink size={25} />
            <Title>Vínculos</Title>
          </LinkTitleWrapper>
          <IoMdClose onClick={handleClose} size={20} />
        </ModalHeader>
        <Subtitle>
          <p><i>Ao vincular ideias, os comentários serão unificados. A ideia que você iniciou o vínculo é a principal.</i></p>
        </Subtitle>
        <LinkedIdeasContainer>
          <div>Ideias complementares</div>
          <p><i>Adicione ideias que possuem um nível de semelhança com a principal mas que não são totalmente dependentes. Você pode desfazer o vínculo a qualquer momento</i></p>
          <LinkedIdeasWrraper>
            {linkedIdeas.length > 0 ? linkedIdeas.map((linkedIdea) =>
              <LinkedIdeaCard key={linkedIdea.id}>
                <IdeaTitleWrapper>
                  <BsCircleFill size={8} />
                  <p>{linkedIdea.title}</p>
                </IdeaTitleWrapper>
                <IdAndCloseWrapper>
                  <p>ID #{getSequenceNumber(linkedIdea.sequence)}</p>
                  <IoMdClose onClick={handleUnlinkidea} size={15} style={{ cursor: "pointer" }} />
                </IdAndCloseWrapper>
              </LinkedIdeaCard>
            ) : null}
          </LinkedIdeasWrraper>
          <AddIdeaWrapper isAdding={isAddingIdea} onClick={() => setIsAddingIdea(true)}>
            <HiOutlinePlus size={16} />
            <p>Adicionar ideia</p>
          </AddIdeaWrapper>
          <SearchIdeasWrapper isAdding={isAddingIdea}>
            <WapperInput>
              <div id="icon">
                <AiOutlineSearch color={colors.font} size={20} />
              </div>
              <InputSearch
                placeholder="Buscar por título, ID ou palavra-chave"
                value={searchState}
                onChange={e => setSearchState(e.target.value)}
                onFocus={handleSearching}
                // onBlur={handleNotSearching}
              />
            </WapperInput>
          </SearchIdeasWrapper>
          <ResultSearcIdeasContainer isSearchingIdea={isSearchingIdea}>
            {ideasForLink.length > 0 ? ideasForLink.map((idea) => 
              <PossibleLinkIdeaCard key={idea.id} onClick={() => handleLinkIdea(idea.id)}>
                <IdeaTitleWrapper>
                  <BsCircleFill size={8} />
                  <p>{buildIdeaTitle(idea.title)}</p>
                </IdeaTitleWrapper>
                <IdAndCloseWrapper>
                  <p>ID #{getSequenceNumber(idea.sequence)}</p>
                </IdAndCloseWrapper>
              </PossibleLinkIdeaCard>
            ): null}
          </ResultSearcIdeasContainer>
        </LinkedIdeasContainer>
      </Container>
    </Modal>
  );
};

And this is my function on Context (ApprovalFunnelContext):

  const listLinkedIdeas = useCallback(async (ideaId: string) => {
    try {
      dispatch({ type: 'SET_LOADING', loading: true });
      const {
        data: {
          linkedIdeas
        }
      } = await api.get(`/idea-links/${ideaId}`);
      dispatch({ type: 'SET_LINKED_IDEAS', linkedIdeas });
      dispatch({ type: 'SET_LOADING', loading: false });
    } catch (err) {
      toast.error(err?.response?.data?.message || 'Erro ao buscar ideias vinculadas');
    }
  }, []);

I can't understand why the handleListLinkedIdeas function on useEffect is causing the loop. When i comment this function, the re-render loop stops (but i dont get the data i need)...

I've already tried to use a State and to put all sort of variables on the array second argument of useEffect.

vinigiu
  • 11
  • 1
  • `listLinkedIdeas` is *already* a memoized callback, I don't see the point in wrapping it in another `useCallback` hook in `LinkIdeasModal`. You can just call `listLinkedIdeas(idea.id)` in the `useEffect` hook. The `useEffect` hook has no dependencies, so if that line creates a render loop then I suspect whatever state that is updated is triggering an ancestor component to rerender and this `LinkIdeasModal` component is remounting. Can you [edit] to provide a more comprehensive [mcve] so we can see more of what the app code is doing? – Drew Reese Aug 12 '23 at 06:18

0 Answers0