oladimeji
oladimeji

Reputation: 111

Fetch data before component render, Reactjs

I am having a slight issue with my react code. I want the data to be completely fetched before rendering. However, I have tried various ways such as 'setGroupLoaded to true' after the async call, but it is still not working. When the component first mounts, 'groupLoaded == false and myGroup == [],', then it goes to the next conditional statement where 'groupLoaded == true' but the myGroup [] is still empty. I was expecting the myGroup [] to be filled with data since groupLoaded is true. Please I need help with it.

function CreateGroup({ currentUser }) {
      const [name, setName] = useState("");
      const [myGroup, setMyGroup] = useState([]);
      const [groupAdded, setGroupAdded] = useState(false);
      const [groupLoaded, setGroupLoaded] = useState(false);
     
    
      const handleChange = (e) => {
        const { value, name } = e.target;
        setName({
          [name]: value,
        });
      };
    
    
      useEffect(() => {
        const fetchGroupCreated = async () => {
          let groupArr = await fetchMyGroup(currentUser);
          setMyGroup(groupArr);
        };
    
        fetchGroupCreated();
    
        setGroupLoaded(true);
    
        return () => {
          setMyGroup([]);
        };
      }, [currentUser.id, groupAdded, deleteGroupStatus]);
    
      useEffect(() => {
        let groupId = myGroup.length ? myGroup[0].id : "";
        let groupName = myGroup.length ? myGroup[0].groupName : "";
        if (myGroup.length) {
          JoinGroup(currentUser, groupId, groupName);
          setTimeout(() => fetchGroupMembers(), 3000);
        }
      }, [myGroup]);
    
      let itemsToRender;
      if (groupLoaded && myGroup.length) {
        
        itemsToRender = myGroup.map((item) => {
          return <Group key={item.id} item={item} deleteGroup={deleteGroup} />;
        });
      } else if (groupLoaded && myGroup.length === 0) {
       
        itemsToRender = (
          <div>
            <form onSubmit={handleSubmit}>
              <div className="form-group">
                <label htmlFor="exampleInputTitle">Group Name</label>
                <input
                  type="text"
                  className="form-control"
                  name="name"
                  id="name"
                  aria-describedby="TitleHelp"
                  onChange={handleChange}
                />
              </div>
    
              <button type="submit" className="btn btn-primary">
                Add group{" "}
              </button>
            </form>
          </div>
        );
      }
      
      return (
        <div>
          <h3>{currentUser ? currentUser.displayName : ""}</h3>
          {itemsToRender}
    
         
        </div>
      );
    }

Upvotes: 0

Views: 66

Answers (1)

Frank van Puffelen
Frank van Puffelen

Reputation: 600131

The problem is here:

  useEffect(() => {
    const fetchGroupCreated = async () => {
      let groupArr = await fetchMyGroup(currentUser);
      setMyGroup(groupArr);
    };

    fetchGroupCreated();

    setGroupLoaded(true);

    return () => {
      setMyGroup([]);
    };
  }, [currentUser.id, groupAdded, deleteGroupStatus]);

When you call setGroupLoaded(true), the first call to setMyGroup(groupArr) hasn't happened yet because fetchMyGroup(currentUser) is asynchronous. If you've never done this before, I highly recommend putting in some logging statements, to see the order in which is executes:

  useEffect(() => {
    const fetchGroupCreated = async () => {
      console.log("Got data")
      let groupArr = await fetchMyGroup(currentUser);
      setMyGroup(groupArr);
    };

    console.log("Before starting to load")
    fetchGroupCreated();

    setGroupLoaded(true);
    console.log("After starting to load")

    return () => {
      setMyGroup([]);
    };
  }, [currentUser.id, groupAdded, deleteGroupStatus]);

The output will be:

Before starting to load

After starting to load

Got data

This is probably not the order you expected, but explains exactly why you get groupLoaded == true, but the myGroup is still empty.

The solution (as Nick commented) is to move the setGroupLoaded(true) into the callback, so that it runs after the data is retrieved:

const fetchGroupCreated = async () => {
  let groupArr = await fetchMyGroup(currentUser);
  setMyGroup(groupArr);
  setGroupLoaded(true);
};

fetchGroupCreated();

An alternative approach may be to await the call to fetchGroupCreated(). I'm not sure if it'll work, but if it does it'll be simpler:

await fetchGroupCreated();

Upvotes: 1

Related Questions