0

I was using useContext() to get the values like name and photo in Navigation.js. I have set the <UserProfileContext.Provider value={{ updateProfile, setUpdateProfile}}> in Profile.js. But still values are not displaying in Navigation.js >> {updateProfile.name}|

App.js

import React, { useState } from 'react';
import { BrowserRouter, Route, Switch } from "react-router-dom";
import Home from "./components/Home";
import Register from "./components/Register";
import Login from "./components/Login";
import Aboutus from "./components/Aboutus";
import Navigation from "./components/Navigation";
import Profile from "./components/Profile";
import ErrorPage from "./components/ErrorPage";
import { ProtectedRoute } from "./components/protected.route";
import UserProfileContext from './context';

var ReactDOM = require("react-dom");

const App = () => {                       

  return (
     <BrowserRouter>
        <>
     <Navigation />
      <Switch>
          <ProtectedRoute exact path="/" component={Home} />
          <ProtectedRoute path="/profile" component={Profile} />
          <ProtectedRoute path="/aboutus" component={Aboutus} />
          <Route path="/register" component={Register} />
          <Route path="/login" component={Login} />
          <Route exact path="*" component={ErrorPage} />
      </Switch>
    </>
   </BrowserRouter>
  );
};
ReactDOM.render(
  React.createElement(App, null),
  document.getElementById("root")
);

export default App;

Navigation.js

import React, { useContext } from 'react';
import { NavLink, useHistory } from 'react-router-dom';
import UserProfileContext from '../context';


const Navigation = () => {
    const history = useHistory();
    const { updateProfile } = useContext(UserProfileContext); 

    const divStyle = {
        float:'left',
        color: '#64cad8', 
        padding: '0px 0px 0px 10px',
        font:'Lucida, sans-serif'
      };

    function logout() {
        localStorage.removeItem('loginEmail')
        localStorage.removeItem('Privilege')
        history.push('/login')
        window.location.reload(true);
      }

    return localStorage.getItem('loginEmail') &&
        <div className="App">
            <div className="wrapper">
                <nav className="siteNavigation_nav_links">
                <div className="clubLogo landing"style={divStyle}><b>Southside Soccer</b></div>
                    <NavLink className="mobile_register_link" to="/">Home</NavLink>
                    <NavLink className="mobile_register_link" to="/profile">Profile</NavLink>
                    <NavLink className="mobile_login_link" to="/login" onClick={logout}>Logout</NavLink>
                    <NavLink className="mobile_login_link" to='/aboutus'>About us</NavLink>
                <div className="profileImage nav menu">
                <span>{updateProfile.name}</span>|<img src={updateProfile.photo}></img>
                </div>
                </nav>
            </div>
        </div>
}

export default Navigation;

context.js

import React from 'react';

export default React.createContext();

Profile.js

import React, {useEffect, useState } from "react";
import { useForm } from 'react-hook-form';
import { useHistory } from "react-router-dom";
import Axios from "axios";
import UserProfileContext from '../context';

const Profile = () => {

  const [email, setEmail] = useState('');
  const [picture, setPicture] = useState('');
  const [playerProfile, setPlayerProfile] = useState([]);
  const loginUserEmail = localStorage.getItem('loginEmail');
  const [updateProfile, setUpdateProfile] = useState({ _id: '', photo: '', name: '', email:''})
  const [isSent, setIsSent] = useState(false);
  const [helperText, setHelperText] = useState('');
  const [disabled, setDisabled] = useState(true);
  const { handleSubmit, register, errors } = useForm();
  const history = useHistory();


  const onChangePicture = e => {
    console.log('picture: ', picture);
    if (e.target.files.length) {
      setPicture(URL.createObjectURL(e.target.files[0]));
    } else {
      return false;
    }
  };

  // If no profile image is being uploaded, to avoid the broken display of image, display a default image.
  const addDefaultSrc = e => {
    e.target.src = '/images/default-icon.png';
  }

  // Pass the id to the handler so you will know which item id changing.
  const handleChange = (e, id) => {
    e.persist();
    let itemIndex;
    const targetPlayer = playerProfile.find((player, index) => {
      console.log({ player, id, index });
      itemIndex = index; 
      return player.id === id;
    });

    console.log({ targetPlayer, id, e });

    const editedTarget = {
      ...targetPlayer,
      [e.target.name]: e.target.value
    };
    const tempPlayers = Array.from(playerProfile);
    tempPlayers[itemIndex] = editedTarget;
    setPlayerProfile(tempPlayers);
    setUpdateProfile({ ...updateProfile, [e.target.name]: e.target.value }); // this is added just to see if its working
  };

  useEffect(() => {
    const fetchData = async () => {
      try {
        const params = {
          email: loginUserEmail,
        };
      const res = await Axios.get('http://localhost:8000/service/profile', {params});
        setPlayerProfile(res.data.playerProfile);
      } catch (e) {
        console.log(e);
      }
    }
    fetchData();
  }, []);

  const onSubmit = () => {
    setDisabled(disabled);
    const fetchData = async () => {
      try {
        const params = {
          email: loginUserEmail,
        };
        const data = {photo: updateProfile.photo, name: updateProfile.name, email: updateProfile.email}
        const res = await Axios.put('http://localhost:8000/service/profile', data, {params}); 
        console.log("Front End update message:" + res.data.success);
        if (res.data.success) {
          setIsSent(true);
          history.push('/')
        }
        else {
          console.log(res.data.message);
          setHelperText(res.data.message);
        }
      } catch (e) {
        setHelperText(e.response.data.message);
      }
    }
    fetchData();
  }

  return (
  <UserProfileContext.Provider value={{ updateProfile, setUpdateProfile}}>
    <div className="register_wrapper">
      <div className="register_player_column_layout_one">
        <div className="register_player_Twocolumn_layout_two">
          <form onSubmit={handleSubmit(onSubmit)} className="myForm">
            {
              playerProfile.map(({ id, photo, name, email}) => (
                <div key={id}>
                  <div className="formInstructionsDiv formElement">
                    <h2 className="formTitle">Profile</h2>
                    <div className="register_profile_image">
                      <input id="profilePic" name="photo" type="file" onChange={onChangePicture} />
                    </div>
                    <div className="previewProfilePic" >
                      <img alt="" onError={addDefaultSrc} name="previewImage" className="playerProfilePic_home_tile" src={photo} onChange={e => handleChange(e, id)}></img>
                    </div>
                  </div>
                  <div className="fillContentDiv formElement">
                    <label>
                      <input className="inputRequest formContentElement" name="name" type="text" value={name} 
                      onChange={e => handleChange(e, id)}
                      maxLength={30}
                      ref={register({
                        required: "Full name is required", 
                        pattern: {
                          value: /^[a-zA-Z\s]{3,30}$/,
                          message: "Full name should have minimum of 3 letters"
                        }
                      })}
                      />
                      <span className="registerErrorTextFormat">{errors.name && errors.name.message}</span>
                    </label>
                    <label>
                      <input className="inputRequest formContentElement" name="email" type="text" value={email} 
                      onChange={e => handleChange(e, id)}
                      disabled={disabled}
                      />
                    </label>
                  </div>
                  <label>
                    <span className="profileValidationText">{helperText}</span>
                  </label>
                  <div className="submitButtonDiv formElement">
                    <button type="submit" className="submitButton">Save</button>
                  </div>
                </div>
              ))
            }
          </form>

        </div>
      </div>
    </div>
  </UserProfileContext.Provider>
  );
}

export default Profile;
soccerway
  • 10,371
  • 19
  • 67
  • 132

1 Answers1

1

Navigation component needs to have the ContextProvider up in the hierarchy but it doesn't as the Provider is rendered within the Profile component.

You must move the usage of Provider out into a separate component and render it as a component ancestor of both Navigation and Profile. Post that you can use UserContext within Navigation and Profile both

UserProfileProvider.js

import UserProfileContext from '../context';
const UserProfileProvider = ({children}) => {
    const [updateProfile, setUpdateProfile] = useState({ _id: '', photo: '', name: '', email:''});

    const value = useMemo(() => ({
       updateProfile, setUpdateProfile
    }), [updateProfile]);

    return (
       <UserProfileContext.Provider value={value}>
           {children}
       </UserProfileContext.Provider>
    )   
}

App.js

import UserProfileProvider from './UserProfileProvider.js';
const App = () => {                       
  return (
     <BrowserRouter>
       <UserProfileProvider>
         <Navigation />
          <Switch>
              <ProtectedRoute exact path="/" component={Home} />
              <ProtectedRoute path="/profile" component={Profile} />
              <ProtectedRoute path="/aboutus" component={Aboutus} />
              <Route path="/register" component={Register} />
              <Route path="/login" component={Login} />
              <Route exact path="*" component={ErrorPage} />
          </Switch>
       </UserProfileProvider>
   </BrowserRouter>
  );
};

Profile.js

import UserProfileContext from '../context';

const Profile = () => {

  const [email, setEmail] = useState('');
  const [picture, setPicture] = useState('');
  const [playerProfile, setPlayerProfile] = useState([]);
  const loginUserEmail = localStorage.getItem('loginEmail');
  const {updateProfile, setUpdateProfile} = useContext(UserProfileContext);
  const [isSent, setIsSent] = useState(false);
  const [helperText, setHelperText] = useState('');
  const [disabled, setDisabled] = useState(true);
  const { handleSubmit, register, errors } = useForm();
  const history = useHistory();


  const onChangePicture = e => {
    console.log('picture: ', picture);
    if (e.target.files.length) {
      setPicture(URL.createObjectURL(e.target.files[0]));
    } else {
      return false;
    }
  };

  // If no profile image is being uploaded, to avoid the broken display of image, display a default image.
  const addDefaultSrc = e => {
    e.target.src = '/images/default-icon.png';
  }

  // Pass the id to the handler so you will know which item id changing.
  const handleChange = (e, id) => {
    e.persist();
    let itemIndex;
    const targetPlayer = playerProfile.find((player, index) => {
      console.log({ player, id, index });
      itemIndex = index; 
      return player.id === id;
    });

    console.log({ targetPlayer, id, e });

    const editedTarget = {
      ...targetPlayer,
      [e.target.name]: e.target.value
    };
    const tempPlayers = Array.from(playerProfile);
    tempPlayers[itemIndex] = editedTarget;
    setPlayerProfile(tempPlayers);
    setUpdateProfile({ ...updateProfile, [e.target.name]: e.target.value }); // this is added just to see if its working
  };

  useEffect(() => {
    const fetchData = async () => {
      try {
        const params = {
          email: loginUserEmail,
        };
      const res = await Axios.get('http://localhost:8000/service/profile', {params});
        setPlayerProfile(res.data.playerProfile);
      } catch (e) {
        console.log(e);
      }
    }
    fetchData();
  }, []);

  const onSubmit = () => {
    setDisabled(disabled);
    const fetchData = async () => {
      try {
        const params = {
          email: loginUserEmail,
        };
        const data = {photo: updateProfile.photo, name: updateProfile.name, email: updateProfile.email}
        const res = await Axios.put('http://localhost:8000/service/profile', data, {params}); 
        console.log("Front End update message:" + res.data.success);
        if (res.data.success) {
          setIsSent(true);
          history.push('/')
        }
        else {
          console.log(res.data.message);
          setHelperText(res.data.message);
        }
      } catch (e) {
        setHelperText(e.response.data.message);
      }
    }
    fetchData();
  }

  return (
    <div className="register_wrapper">
      <div className="register_player_column_layout_one">
        <div className="register_player_Twocolumn_layout_two">
          <form onSubmit={handleSubmit(onSubmit)} className="myForm">
            {
              playerProfile.map(({ id, photo, name, email}) => (
                <div key={id}>
                  <div className="formInstructionsDiv formElement">
                    <h2 className="formTitle">Profile</h2>
                    <div className="register_profile_image">
                      <input id="profilePic" name="photo" type="file" onChange={onChangePicture} />
                    </div>
                    <div className="previewProfilePic" >
                      <img alt="" onError={addDefaultSrc} name="previewImage" className="playerProfilePic_home_tile" src={photo} onChange={e => handleChange(e, id)}></img>
                    </div>
                  </div>
                  <div className="fillContentDiv formElement">
                    <label>
                      <input className="inputRequest formContentElement" name="name" type="text" value={name} 
                      onChange={e => handleChange(e, id)}
                      maxLength={30}
                      ref={register({
                        required: "Full name is required", 
                        pattern: {
                          value: /^[a-zA-Z\s]{3,30}$/,
                          message: "Full name should have minimum of 3 letters"
                        }
                      })}
                      />
                      <span className="registerErrorTextFormat">{errors.name && errors.name.message}</span>
                    </label>
                    <label>
                      <input className="inputRequest formContentElement" name="email" type="text" value={email} 
                      onChange={e => handleChange(e, id)}
                      disabled={disabled}
                      />
                    </label>
                  </div>
                  <label>
                    <span className="profileValidationText">{helperText}</span>
                  </label>
                  <div className="submitButtonDiv formElement">
                    <button type="submit" className="submitButton">Save</button>
                  </div>
                </div>
              ))
            }
          </form>

        </div>
      </div>
    </div>
  );
}

export default Profile;
Shubham Khatri
  • 270,417
  • 55
  • 406
  • 400
  • But if we move out to separate component... `const [updateProfile, setUpdateProfile] = useState({ _id: '', photo: '', name: '', email:''});` , as this is used get `data` for sending `const res = await Axios.put('http://localhost:8000/service/profile', data, {params});` .. – soccerway Jun 16 '20 at 12:36
  • 1
    Its using the data from context. Please carefully look at the usage in profile. Its used like `const {updateProfile, setUpdateProfile} = useContext(UserProfileContext);` and not as a state – Shubham Khatri Jun 16 '20 at 12:41
  • Now in Navigation how do we display the name and photo ? Should I need `import UserProfileContext from '../context'; ` and then ` const { updateProfile } = useContext(UserProfileContext);` – soccerway Jun 16 '20 at 13:03
  • Get the values like this ? {updateProfile.name}| – soccerway Jun 16 '20 at 13:04
  • In `Navigation.js` I have `import UserProfileContext from '../context';` and then `const { updateProfile } = useContext(UserProfileContext); ` But still i couldn't see the name and photo in these fields .. {updateProfile.name}| – soccerway Jun 16 '20 at 13:17
  • 1
    use you will update with `useContext` in both Profile and Navigation. also make sure the data is being updated correctly from Profile to Context so that you can access it in Navigation. Although you must note that unless to update the data from profile you won't see it in Navigation – Shubham Khatri Jun 16 '20 at 15:14
  • While updating I can see the `name`, but I am unable to see photo..... Also while refreshing the screen the name got disappeared, the same thing happening after logout and login back I am unable to see the name. – soccerway Jun 17 '20 at 05:27
  • 1
    Context doesn't preserve data on refreshes, you would need to store it in localStorage or some other browserStorage – Shubham Khatri Jun 17 '20 at 05:29
  • Ok thanks, can we store photo too in localStorage ? – soccerway Jun 17 '20 at 05:31
  • 1
    You can either store base64 format of image file in localStorage – Shubham Khatri Jun 17 '20 at 05:32
  • Any idea why in my case photo is not displaying at all, it shows src(unknown)...? – soccerway Jun 17 '20 at 05:33
  • I think you didn't add photo in profile info as the logic is not in handleChange function which updates the context – Shubham Khatri Jun 17 '20 at 05:42