Search code examples
javascriptreactjsmeteoruse-state

How to I wrap a useState variable in a if statment, but still have it's value be available outside the if reactjs


I have the following code I have a cards state variable using useState, I have atttempted to add my array above to it, but it just adds an empty array, I wasn't able to put the state inside of the if becuase then my variable was undefined. I tried wrapping everything beflow the state and the state in the if , but the then I get some return issues. So the focus is passing into the useState(stateReplace)

Any help would be great

import React, { useState, useCallback, useEffect, useMemo } from "react";
import { Card } from "./Card";
import update from "immutability-helper";
import { LeadsBuilderCollection } from "../../api/LeadsCollection";
import { useTracker } from "meteor/react-meteor-data";

const style = {
  width: 400,
};
export const Container = ({ params }) => {
  const { leadsBuilder, isLoading } = useTracker(() => {
    const noDataAvailable = { leadsBuilder: [] };

    if (!Meteor.user()) {
      return noDataAvailable;
    }

    const handler = Meteor.subscribe("leadsBuilder");

    if (!handler.ready()) {
      return { ...noDataAvailable, isLoading: true };
    }

    const leadsBuilder = LeadsBuilderCollection.findOne({ _id: params._id });
    return { leadsBuilder };
  });
  const [cards, setCards] = useState([]);

  let stateReplace = useMemo(() => {
    if (!isLoading && leadsBuilder?.inputs?.length) {
      leadsBuilder.inputs.map((leadInput, i) => {
        ({ id: i, text: leadInput.name });
      });
    }
    return [];
  }, [isLoading, leadsBuilder]);

  useEffect(() => {
    setCards(stateReplace);
  }, [setCards, stateReplace]);

  const moveCard = useCallback(
    (dragIndex, hoverIndex) => {
      const dragCard = cards[dragIndex];
      setCards(
        update(cards, {
          $splice: [
            [dragIndex, 1],
            [hoverIndex, 0, dragCard],
          ],
        })
      );
    },
    [cards]
  );
  const renderCard = (card, index) => {
    return (
      <>
        {isLoading ? (
          <div className="loading">loading...</div>
        ) : (
          <>
            <Card
              key={card.id}
              index={index}
              id={card.id}
              text={card.text}
              moveCard={moveCard}
            />
          </>
        )}
      </>
    );
  };
  return (
    <>
      {isLoading ? (
        <div className="loading">loading...</div>
      ) : (
        <>
          <div style={style}>{cards.map((card, i) => renderCard(card, i))}</div>
        </>
      )}
    </>
  );
};

Update: I can get it to run if I place a setState in a useEffect but then I get a warning and the drag and drop doesnt work

 useEffect(() => {
    setCards(stateReplace);
  });

Maximum update depth exceeded. This can happen when a component calls setState inside useEffect, but useEffect either doesn't have a dependency array, or one of the dependencies changes on every render.

Update #2

  const [cards, setCards] = useState([]);

  let stateReplace = useMemo(() => {
    console.log("memo");
    if (!isLoading && leadsBuilder?.inputs?.length) {
      return leadsBuilder.inputs.map((leadInput, i) => {
        ({ id: i, text: leadInput.name });
      });
    }
    return [];
  }, [isLoading]);

  console.log(stateReplace);

  useEffect(() => {
    setCards(stateReplace);

    console.log(setCards);
  }, [setCards, stateReplace]);

current output

(4) [undefined, undefined, undefined, undefined]
memo
cannot read propery `id`

Solution

  • i would do it like that

    //for preveting updates of memo if ledsBulder will changes on each render
    const leadsBuilderRef = useRef(leadsBuilder)
    
    let stateReplace = useMemo(()=>{
      if (!isLoading && leadsBuilder.current?.inputs?.length) {
        return leadsBuilder.current.inputs.map((leadInput, i) => {
          return { id: i, text: leadInput.name };
        });
      }
      return []
    }, [isLoading, leadsBuilderRef]);
    
    

    and then

    useEffect(() => {
       setCards(stateReplace);
    }, [setCards, stateRepalce]);