0

I have a User page component which has a link that is meant to open a FollowingModal modal component to show the followers of a user.

The User component calls an async function in my followers context to retieve the user's details of the user page I am on and uses the data to update a userDetails state hook.

I'm then attempting to pass the data in the state object to mt FollowingModal as props but the modal always shows these props as undefined.

I presume this is something like the modal is being rendered before the state is updated, but I'm not sure how to go about this in order to get the desired result of these props being properly initialized and passed to the modal component

Here's my current code (minus all the irrelevant functionality I've stripped out)

User.jsx:

import { useParams } from 'react-router-dom';
import { Container, Row, Col, Button, Tabs, Tab } from 'react-bootstrap';
import { useAuth } from '../contexts/FollowersContext';
import { FollowingModal } from './partials/user/FollowingModal';
import { ProfileTabAddedPages } from './partials/ProfileTabAddedPages';
import { ProfileTabLikedPages } from './partials/ProfileTabLikedPages';

export function User() {
  const { username } = useParams();
  const {
    checkIsFollowing,
    getUserImageById,
    getUserProfileByUsername,
  } = useAuth();

  const handleCloseFollowingDialog = () => setShowFollowingDialog(false);
  const handleSetShowFollowingDialog = () => setShowFollowingDialog(true);
  const [showFollowingDialog, setShowFollowingDialog] = useState(false);
  const [userDetails, setUserDetails] = useState([]);
  const [loadingUserDetails, setLoadingUserDetails] = useState(true);

  const getUserDetails = async () => {
    try {
      const data = await getUserProfileByUsername(username);
      setUserDetails(data);
      setLoadingUserDetails(false);
    } catch (error) {
      console.log('Error retrieving user profile' + error);
      setLoadingUserDetails(false);
    }
  };

  useEffect(() => {
    getUserDetails();
    // eslint-disable-next-line
  }, [loadingUserDetails]);

  return (
    <div className="container-md clear-header-footer">
      <Container flex>
        <FollowingModal
          showFollowingDialog={showFollowingDialog}
          onHideFollowingDialog={handleCloseFollowingDialog}
          userid={userDetails?.id}
          username={userDetails?.username}
        ></FollowingModal>
        <Row>
          <Col>
            <h1 className="page-heading">profile</h1>
          </Col>
        </Row>
        <Row>
          <Col>
            <div className="mx-auto image-placeholder-profile">
            </div>
          </Col>
          <Col>
            <h2>{userDetails ? userDetails.displayName : 'display name'}</h2>
            <h5>@{userDetails ? userDetails.username : 'username'}</h5>
            <p>{userDetails ? userDetails.bio : 'bio'}</p>
            <div
              onClick={() => handleSetShowFollowingDialog()}
              className="clickable-text fit-content"
            >
              <p>following</p>
            </div>
          </Col>
        </Row>
      </Container>
    </div>
  );
}

FollowingModal.jsx:

import React, { useRef, useState, useEffect, Fragment } from 'react';
import { useAuth as useFollowerContext } from '../../../contexts/FollowersContext';
import { Modal, Card, Col} from 'react-bootstrap';

export function FollowingModal(props) {
  const {
    getUserFollowing,
  } = useFollowerContext();
  const [following, setFollowing] = useState([]);
  const [loadingFollowers, setLoadingFollowing] = useState(true);

  const getFollowing = async () => {
    try {
      // <----- props?.userid is always undefined at this point ----->
      const data = await getUserFollowing(props?.userid);
      setFollowing(data);
      setLoadingFollowing(false);
    } catch (error) {
      console.log('getFollowing() error: ' + error);
    }
  };

  useEffect(() => {
    getFollowing();
    // eslint-disable-next-line
  }, [loadingFollowers]);

  return (
    <Fragment>
      <Modal
        show={props.showFollowingDialog}
        onHide={props.onHideFollowingDialog}
        userid={props.userid}
        centered
      >
          <Modal.Header closeButton>
            <Modal.Title>Following</Modal.Title>
          </Modal.Header>
          <Modal.Body>
            {following?.map((follower, index) => (
              <Col key={index}>
                <Card>
                  <Card.Body>
                    <span>{follower?.username}</span>
                  </Card.Body>
                </Card>
              </Col>
            ))}
          </Modal.Body>
      </Modal>
    </Fragment>
  );
}

getUserFollowing() (in FollowersContext.js):

const getUserFollowing = async (id) => {
    try {
      const usersFollowingRef = query(
        collection(db, 'users', id, 'following')
      );
      const usersFollowingSnapshot = await getDocs(usersFollowingRef);

      if (!usersFollowingSnapshot.empty) {
        return usersFollowingSnapshot.docs.map((doc) => doc.data());
      }
    } catch (error) {
      console.error(error);
    }
  };
Greg
  • 187
  • 3
  • 15

1 Answers1

0

Managed to fix it in the end. I changed the user.jsx component so that it check for the userDetails.id value before rendering the modal component:

{userDetails?.id && (
     <FollowingModal
               showFollowingDialog={showFollowingDialog}
               onHideFollowingDialog={handleCloseFollowingDialog}
               userid={userDetails?.id}
               username={userDetails?.username}
        ></FollowingModal>
)}
Greg
  • 187
  • 3
  • 15