2

My main objective is to conditionally render a "Create profile" button if the user does not have a profile in firebase, or an "Edit profile" button if the user has a profile.

I am not having issues rendering the edit profile button. I am checking if the user has a profile by comparing the auth users "uid" with the profile's "uid." If they match, then the edit button will render.

My problem is that, when the edit button renders, the create button still appears as well, but it should disappear since I am conditionally rendering the two.

What am I missing here?

EDIT 3/23/2018

I've figured out the problem but I still don't have a solution. The problem is that the map function is looping through the 'Profiles' array and is looking for a 'uid' equal to to the logged in users 'uid'.

But if the 'uid' doesn't match the logged in users 'uid,' the create button will still render since there are other profiles in the 'Profiles' array which have uid's not equal to the logged in users 'uid.'

So i guess my other question would be,

how can I check if a logged in user does not have data in an array and/or Firebase db?

Here's my code:

I have database named 'Profiles' which I am pulling information from.

"Profiles" : {
"-L7p-wZNcvgBBTkvmn7I" : {
  "about" : "I'm a full stack web developer with many skills",
  "email" : "email@gmail.com",
  "frameworkOne" : "react",
  "frameworkThree" : "bootstrap",
  "frameworkTwo" : "javascript",
  "name" : "Dylan Prem",
  "projectInfo" : "You're on it!",
  "projectLink" : "https://hiremoredevs.com",
  "projectName" : "HireMoreDevs",
  "uid" : "ABCDEFG1234567"
}

}

The react component:

class ProfileButtonToggle extends Component {
constructor(props){
    super(props);
    this.state = {
        authUser:null,
        Profiles:[]

    }
}

componentDidMount(){
  const profilesRef = firebase.database().ref('Profiles');
    profilesRef.once('value', (snapshot) => {
    let Profiles = snapshot.val();
    let newState = [];
    for (let profile in Profiles){
      newState.push({
        id: profile,
        uid:Profiles[profile].uid,
      });
    }
    this.setState({
      Profiles: newState
    });
  });   

  firebase.auth().onAuthStateChanged((authUser) => {
      if (authUser) {
        this.setState({ authUser });
      } 
    }); 
}


render() {
    return (
        <div>
        {this.state.authUser ?
                <div>
                    {this.state.Profiles.map((profile) => {
                        return(
                        <div key={profile.id}>
                            {profile.uid === this.state.authUser.uid ?
                            <Nav>
                                <NavItem>
                                    <Link className='btn yellow-button' to={`/edit/${profile.id}`}>Edit Profile</Link>
                                </NavItem>
                            </Nav>
                            :
                            <Nav>
                                <NavItem>
                                    <Link className='btn yellow-button' to={routes.CREATE_PROFILE}>Create Profile</Link>
                                </NavItem>
                            </Nav>
                        }
                        </div>
                    );
                    })}
                </div>
            :
            null
        }
        </div>

    );
  }
}

export default ProfileButtonToggle;
Dylan Prem
  • 174
  • 11
  • I can't see anything that stands out. How many profiles are you mapping through? It might improve readability to move the `Nav` and `NavItem` outside of the condition as these two components are always rendered. The condition is really only to determine which `Link` to render – brentatkins Mar 18 '18 at 17:32
  • 1
    Can you show us what the contents of `this.state.Profiles` looks like? It looks like you are iterating over this twice, and once it will pass the condition for the edit button, and on the next iteration it still passes the condition for the create button. – user3692823 Mar 18 '18 at 20:34
  • Looks fine. Perhaps add an Id to each map item so that you can check it really is rendering both. You are mapping over an array of users so maybe its showing 'edit' for one user and 'create' for another user. – Josh Pittman Mar 19 '18 at 01:57
  • @brentatkins I have 2 profiles in the Profiles db, both with different uids. I will give that a shot and let you know how it turns out. – Dylan Prem Mar 19 '18 at 13:20
  • @user3692823 wouldn't I want to iterate through it twice? to check for 2 conditions. – Dylan Prem Mar 19 '18 at 13:22
  • @JoshPittman Good point. So then I guess I should find a way to check if a user doesn't have a profile by some other means. I did try profile.uid !== this.state.authUser.uid but that renders nothing for the create button. – Dylan Prem Mar 19 '18 at 13:30
  • I just meant `{this.state.Profiles.map((profile, index) => { return(
    ...
    ); })} to see if you are rendering what you think you are rendering. Each item should now have a id of 0 to what ever number of items you have. It's a long shot but its simple to rule out.
    – Josh Pittman Mar 20 '18 at 11:04

2 Answers2

2

After weeks of hair pulling, I've finally figured out a solution. It turns out I don't even need a 'create' button.

componentDidUpdate fires after the DOM is rendered which is exaclty what I needed. I then check if a child of 'Profiles' has a uid equal to the logged in users uid, if not push() that current users uid to 'Profiles' and reload the page. And it actually worked!

In other words, it will automatically create a Profile, if one does not exist for this user.

componentDidUpdate() {
    firebase.database().ref("Profiles").orderByChild("uid").equalTo(this.state.authUser.uid).once("value",snapshot => {
        const userData = snapshot.val();
        if (userData){
          console.log("exists!");
        } else {
            const profilesRef = firebase.database().ref('Profiles');
            const Profiles  = {
                uid: this.state.authUser.uid
            }
            profilesRef.push(Profiles);
            window.location.reload();
        }
    });
}

I changed my conditional render to this:

        <div>
        {this.state.authUser ?
            <Nav>
                {this.state.Profiles.map((profile) => {
                    return(
                    <NavItem key={profile.id}>
                            {profile.uid === this.state.authUser.uid ?
                                <Link id='edit-button' className='btn yellow-button job-text' to={`/edit/${profile.id}`}>Edit Profile</Link>
                                : 
                                null
                            }
                    </NavItem>
                );
                })}
            </Nav>
            :
            null
        }
        </div>

After the page reloaded the edit button rendered as expected.

The full component

class ProfileButtonToggle extends Component {
constructor(props){
    super(props);
    this.state = {
        authUser:null,
        Profiles:[],
    }
}




componentDidMount(){
    const profilesRef = firebase.database().ref('Profiles');
        profilesRef.once('value', (snapshot) => {       
            let Profiles = snapshot.val();
            let newState = [];
            for (let profile in Profiles){
              newState.push({
                id: profile,
                uid:Profiles[profile].uid,
              });

            }
            this.setState({
              Profiles: newState
            });
          });

    firebase.auth().onAuthStateChanged((authUser) => {
      if (authUser) {
        this.setState({ authUser });

        
        } 
    }); 
}

componentDidUpdate() {
    firebase.database().ref("Profiles").orderByChild("uid").equalTo(this.state.authUser.uid).once("value",snapshot => {
        const userData = snapshot.val();
        if (userData){
          console.log("exists!");
        } else {
            const profilesRef = firebase.database().ref('Profiles');
            const Profiles  = {
                uid: this.state.authUser.uid
            }
            profilesRef.push(Profiles);
            window.location.reload();
        }
    });
}


render() {
    return (
        <div>
        {this.state.authUser ?
            <Nav>
                {this.state.Profiles.map((profile) => {
                    return(
                    <NavItem key={profile.id}>
                            {profile.uid === this.state.authUser.uid ?
                                <Link id='edit-button' className='btn yellow-button job-text' to={`/edit/${profile.id}`}>Edit Profile</Link>
                                : 
                                null
                            }
                    </NavItem>
                );
                })}
            </Nav>
            :
            null
        }
        </div>
      );
    }
 }

 export default ProfileButtonToggle;
Community
  • 1
  • 1
Dylan Prem
  • 174
  • 11
1

Just a quick tip, your conditional render could use the && logical operator since it doesn't return anything in the else part of the ternary operator

<div>
        {this.state.authUser &&
            <Nav>
                {this.state.Profiles.map((profile) => {
                    return(
                    <NavItem key={profile.id}>
                            {profile.uid === this.state.authUser.uid ?
                                <Link id='edit-button' className='btn yellow-button job-text' to={`/edit/${profile.id}`}>Edit Profile</Link>
                                : 
                                null
                            }
                    </NavItem>
                );
                })}
            </Nav>
        }
        </div>

So It would render either the items after the && or null if this.state.authUser is unavailable

Edgar John
  • 45
  • 6