Rhythm Shandlya
Rhythm Shandlya

Reputation: 124

How to set the data from my API as initial state for my react component?

I am making the profile page for my website, but when I initially render components the data from the API somehow doesn't load up in the initial 'state' I made for About, Blog or Stats.

enter image description here

However when I click on About again the the API data is rendered.

enter image description here

IDK if I am able to make my point, but this is how my react functional component looks like:

const Profile = () => {
    let history = useHistory();
    const [user, setUser] = useState({});
    useEffect(() => {
        async function fetchMyAPI() {
           try {
               let res = await api.get('user/isLoggedIn', { withCredentials: true });
               if (res.data.user) {
                   setUser(res.data.user);
               }
           }catch (err) {
                history.push('/login');
           }
       }
       fetchMyAPI();
    }, []);

    const About = ({ user }) => {
        if (user)
            return (
                <div className="about_wrapper">
                    <p className="name_about">{`${user.name}`}</p>
                    <p className="email_about">{`${user.email}`}</p>
                    <p><FontAwesomeIcon icon={faPen} />{`  ${user.niche}`}</p>
                    <p><FontAwesomeIcon icon={faAlignLeft} />{` ${user.job}`}</p>
                    <p className="description_about"><FontAwesomeIcon icon={faAddressBook} />{` ${user.description}`}</p>
                </div>
            );
        else
            return (
                <h3>Loading..</h3>
            );
}
    const Blogs = (props) => {
        return (
            <>
            </>
        );
    }
    const Stats = (props) => {
        return (
            <>
            </>
        );
    }

    const [state, stateSetter] = useState(<About user={user}/>);

    const clickHandler = (e) => {
        e.preventDefault()
        const nav = document.querySelectorAll('.info_links');
        switch (e.target.textContent) {
            case 'About':
                nav[1].classList.remove('un-p');
                nav[2].classList.remove('un-p');
                stateSetter(<About user={user}/>);
                break;
            case 'Stats':
                nav[0].classList.remove('un-p');
                nav[1].classList.remove('un-p');
                stateSetter(<Stats user={user}/>);
                break;
            case 'Blogs':
                nav[0].classList.remove('un-p');
                nav[2].classList.remove('un-p');
                stateSetter(<Blogs user={user}/>);
                break;
            default:
             console.log('error')
        }
        e.target.classList.add('un-p');
    }

    return (
        <>
            <Navbar />
            <div>
                <div className="CoverImage FlexEmbed FlexEmbed--2by1" style={{ backgroundImage: `url(${user.cover}})` }}></div>
                <img className="avatar" src={`${user.dp}`} alt="Girl in a jacket" />
                <div className="info">
                    <p className="info_links link-grow un un-p" onClick={clickHandler}>About</p>
                    <p className="info_links un" onClick={clickHandler}>Blogs</p>
                    <p className="info_links un" onClick={clickHandler}>Stats</p>
                </div>
                {state}
            </div>
        </>
    );
}

export default Profile

I want to set the data from my API as initial state for my react component!

Upvotes: 2

Views: 2478

Answers (1)

Drew Reese
Drew Reese

Reputation: 202667

Few issues:

  1. You are providing a truthy initial value for the user state so the conditional rendering isn't working as you are probably expecting. I.E. since user is ({}) the if (user) check is true and you attempt to render the undefined properties.
  2. You are storing JSX in component state, an anti-pattern in React.
  3. You are declaring React components in the body of a function component, this will have the effect of creating new components each render cycle and unmount & remount the components. This can have adverse effects on performance.

Move About, Blogs, and Stats out of Profile component so they have stable references.

const About = ({ user }) => {
  if (user)
    return (
      <div className="about_wrapper">
        <p className="name_about">{`${user.name}`}</p>
        <p className="email_about">{`${user.email}`}</p>
        <p><FontAwesomeIcon icon={faPen} />{`${user.niche}`}</p>
        <p><FontAwesomeIcon icon={faAlignLeft} />{`${user.job}`}</p>
        <p className="description_about">
          <FontAwesomeIcon icon={faAddressBook} />
          {`${user.description}`}
        </p>
      </div>
    );
  else
    return (
      <h3>Loading..</h3>
    );
};
const Blogs = (props) => {
  return (
    <>
    </>
  );
};
const Stats = (props) => {
  return (
    <>
    </>
  );
};

Store the component "type" in state and conditionally render them, passing the user state to them.

const Profile = () => {
  const history = useHistory();
  const [user, setUser] = useState(); // <-- undefined, falsey
  const [state, stateSetter] = useState('About'); // <-- use type

  useEffect(() => {
    async function fetchMyAPI() {
      try {
        let res = await api.get('user/isLoggedIn', { withCredentials: true });
        if (res.data.user) {
          setUser(res.data.user);
        }
      } catch(err) {
        history.push('/login');
      }
    }
    fetchMyAPI();
  }, []);

  const clickHandler = (e) => {
    e.preventDefault()
    const nav = document.querySelectorAll('.info_links');
    switch (e.target.textContent) {
      case 'About':
        nav[1].classList.remove('un-p');
        nav[2].classList.remove('un-p');
        stateSetter('About'); // <-- update state type
        break;
      case 'Stats':
        nav[0].classList.remove('un-p');
        nav[1].classList.remove('un-p');
        stateSetter('Stats'); // <-- update state type
        break;
      case 'Blogs':
        nav[0].classList.remove('un-p');
        nav[2].classList.remove('un-p');
        stateSetter('Blogs'); // <-- update state type
        break;
      default:
        console.log('error')
    }
    e.target.classList.add('un-p');
  }

  return (
    <>
      <Navbar />
      <div>
        <div
          className="CoverImage FlexEmbed FlexEmbed--2by1"
          style={{ backgroundImage: `url(${user.cover}})` }}>
        </div>
        <img className="avatar" src={`${user.dp}`} alt="Girl in a jacket" />
        <div className="info">
          <p className="info_links link-grow un un-p" onClick={clickHandler}>About</p>
          <p className="info_links un" onClick={clickHandler}>Blogs</p>
          <p className="info_links un" onClick={clickHandler}>Stats</p>
        </div>
        {state === 'About' && <About user={user} />}
        {state === 'Stats' && <Stats user={user} />}
        {state === 'Blogs' && <Blogs user={user} />}
      </div>
    </>
  );
}

Upvotes: 1

Related Questions