Search code examples
reactjsasynchronousfragmentes6-promisedispatch

How to use data from a Promise in <Fragment> React


I need to get the name of the current user in my React app, so I sent the data from the action/auth.js to Navbar.js, in the Navbar I got a Promise, so I user then to get the data. and I managed to console.log the data. yet I can't use it in order to show it to the user. Here's my auth.js function for sending the data:

export const get_user_data = () => async dispatch => {
    if (localStorage.getItem('access')) {
    const config = {
            headers: {
                'Content-Type': 'application/json',
                'Authorization': `JWT ${localStorage.getItem('access')}`,
                'Accept': 'application/json'
            }
        };
         try {
            const res = await axios.get(`${process.env.REACT_APP_API_URL}/auth/users/me/`, config);
            let name = res.data.first_name +" "+ res.data.last_name;
            return name;


        } catch (err) {

            return err;
        }
    } else {
        return "else accesss"
    }
};

And this is my Navebar.js where I'm trying to show the data:

import React, { Fragment } from 'react';
import { Link } from 'react-router-dom';
import { connect } from 'react-redux';
import { logout, get_user_data } from '../actions/auth';


const Navbar = ({ get_user_data,logout, isAuthenticated}) => {

    console.log("get_user_data", get_user_data().then(res=>{
    console.log("res", res)}));

    const get_name = () => (
    get_user_data().then(res=> {return res})
    );

    const guestLinks = () => (
        <Fragment>
            <li className='nav-item'>
                <Link className='nav-link' to='/login'>לכניסה</Link>
            </li>
            <li className='nav-item'>
                <Link className='nav-link' to='/signup'>להרשמה</Link>
            </li>
        </Fragment>
    );

    const authLinks = () => (

    <Fragment>
        <li className='nav-item'>
            <a className='nav-link' href='#!' onClick={logout}>ליציאה</a>
        </li>
        <li className='nav-item'>
                <Link className='nav-link' to='/homepage'>העסק שלי</Link>
        </li>
        {<li className='nav-item'>
            <Link className='nav-link'>ברוך הבא {get_name()}</Link>
        </li>}


    </Fragment>
    );

    return (

            <nav className='navbar navbar-expand-lg navbar-light bg-light' lang="he" dir="rtl">
                <Link className='navbar-brand' to='/'>החשמלאי</Link>
                <button
                    className='navbar-toggler'
                    type='button'
                    data-toggle='collapse'
                    data-target='#navbarNav'
                    aria-controls='navbarNav'
                    aria-expanded='false'
                    aria-label='Toggle navigation'
                >
                    <span className='navbar-toggler-icon'></span>
                </button>
                <div className='collapse navbar-collapse' id='navbarNav'>
                    <ul className='navbar-nav'>
                        <li className='nav-item active'>
                            <Link className='nav-link' to='/'>עמוד הבית <span className='sr-only'>(current)</span></Link>
                        </li>

                        {isAuthenticated ? authLinks() : guestLinks()}
                    </ul>
                </div>
            </nav>
    );
};

const mapStateToProps = state => ({
    isAuthenticated: state.auth.isAuthenticated
    });

export default connect(mapStateToProps, { logout, get_user_data })(Navbar);

I used get_name() function to parse the data fron the promise, but I still get the Promise error.

The React Error:

react-dom.development.js:13231 Uncaught Error: Objects are not valid as a React child (found: [object Promise]). If you meant to render a collection of children, use an array instead.

Thank you!


Solution

  • Analysis

    The behavior you're getting is because you're trying to display a Promise. You'd need to wait for the Promise to be resolved before displaying it. Here I'll use the useState and useEffect hook to achieve the behavior I am talking about.

    Solution

    First, you'll need a state to hold your userData. From your code in the get_user_data function, let's assume the data we're talking about is the user name. The default value of this state will be an empty string.

    const [name, setName] = useState('');
    

    Then, I'll create an useEffect hook to use your get_user_data function to populate the newly created name state:

    useEffect(() => {
      get_user_data().then((data) => {
        setName(data);
      });
    }, []);
    

    This hook will get the data and set the name state when the fetch is finished.

    Then we'll need to use this name state wherever we need. The behavior of this component is that it'll display '' when we're getting the user name, but once the data is available the UI will display it. Putting it all together, we have:

    import React, { Fragment, useState, useEffect } from 'react';
    import { Link } from 'react-router-dom';
    import { connect } from 'react-redux';
    import { logout, get_user_data } from '../actions/auth';
    
    
    const Navbar = ({ get_user_data,logout, isAuthenticated}) => {
        const [name, setName] = useState('');
    
        useEffect(() => {
          get_user_data().then((data) => {
            setName(data);
          });
        }, []);
    
        const guestLinks = () => (
            <Fragment>
                <li className='nav-item'>
                    <Link className='nav-link' to='/login'>לכניסה</Link>
                </li>
                <li className='nav-item'>
                    <Link className='nav-link' to='/signup'>להרשמה</Link>
                </li>
            </Fragment>
        );
    
        const authLinks = () => (
    
        <Fragment>
            <li className='nav-item'>
                <a className='nav-link' href='#!' onClick={logout}>ליציאה</a>
            </li>
            <li className='nav-item'>
                    <Link className='nav-link' to='/homepage'>העסק שלי</Link>
            </li>
            {<li className='nav-item'>
                <Link className='nav-link'>ברוך הבא {get_name()}</Link>
            </li>}
    
    
        </Fragment>
        );
    
        return (
    
                <nav className='navbar navbar-expand-lg navbar-light bg-light' lang="he" dir="rtl">
                    <Link className='navbar-brand' to='/'>החשמלאי</Link>
                    <button
                        className='navbar-toggler'
                        type='button'
                        data-toggle='collapse'
                        data-target='#navbarNav'
                        aria-controls='navbarNav'
                        aria-expanded='false'
                        aria-label='Toggle navigation'
                    >
                        <span className='navbar-toggler-icon'></span>
                    </button>
                    <div className='collapse navbar-collapse' id='navbarNav'>
                        <ul className='navbar-nav'>
                            <li className='nav-item active'>
                                <Link className='nav-link' to='/'>עמוד הבית <span className='sr-only'>(current)</span></Link>
                            </li>
    
                            {isAuthenticated ? authLinks() : guestLinks()}
                        </ul>
                    </div>
                </nav>
        );
    };
    
    const mapStateToProps = state => ({
        isAuthenticated: state.auth.isAuthenticated
    });
    
    export default connect(mapStateToProps, { logout, get_user_data })(Navbar);