MSOACC
MSOACC

Reputation: 3665

React component that takes state variable as a parameter not rendering on update

I have a React component that makes an AJAX call, sets a state variable and then passes that state variable into a component.

Main component:

export default function MyView({ assetId }) {
   const [asset, setAsset] = useState();
   const [beanstalk, setBeanstalk] = useState();
   const [loading, setLoading] = useState(true);

   useEffect(() => {
      loadAsset();
   }, []);

   const loadAsset = async () => {
        const _asset = await findAssetById(assetId);
        setLoading(false);
        if(_asset) {
            setAsset(_asset);
            await loadBeanstalk(_asset.id);
        }
    };

    const loadBeanstalk = async (assetId) => {
        const _beanstalk = await getBeanstalkFromDB(assetId);
        setBeanstalk(_beanstalk);
    };

   return (
      { loading ? (
         <div>Loading...</div>
      ) : (
         <h3>{asset.name}</h3>
         <ChildComponent beanstalk={beanstalk} />
      )}
   )
}

Child component:

export default function ChildComponent({ beanstalk }) {
   const [loadbalancer, setLoadbalancer] = useState([]);
   const [loading, setLoading] = useState(true);

   useEffect(() => {
      console.log("Beanstalk will print as undefined here:");
      console.log(beanstalk);
      getLoadbalancer();
   }, []);

   const getLoadbalancer = async () => {
      if(beanstalk) {
         const _loadbalancer = await getLoadbalancer(beanstalk);
         setLoading(false);
         setLoadbalancer(_loadbalancer);
      }
   }

   return (
      { loading ? (
         <div>Loading...</div>
      ) : (
         <h3>{loadbalancer.name}</h3>
      )}
   )
}

My understanding is that the main component renders when state changes, so when I call setBeanstalk(_beanstalk) it should re-render with the beanstalk state variable set... so why does ChildComponent always render the Beanstalk as null when I know it's being returned from the database? I know it has something to do with the order of rendering.

Upvotes: 1

Views: 240

Answers (1)

segFault
segFault

Reputation: 4054

So the issue seems to be one of order of operations. What you can try to do is one of the following:

Move your setLoading(false) call into loadBeanstalk() so that your ChildComponent only renders once you have fully loaded the stuff.

const loadAsset = async () => {
    const _asset = await findAssetById(assetId);
    if(_asset) {
        setAsset(_asset);
        await loadBeanstalk(_asset.id);
    } else {
        setLoading(false);
    }
};

const loadBeanstalk = async (assetId) => {
    const _beanstalk = await getBeanstalkFromDB(assetId);
    setBeanstalk(_beanstalk);
    setLoading(false);
};

Attach beanstalk to your useEffect hook in ChildComponent.

useEffect(() => {
  console.log("Beanstalk will print as undefined here:");
  console.log(beanstalk);
  getLoadbalancer();
}, [ beanstalk ]);

Upvotes: 1

Related Questions