I'm trying to do some small exercises in react, but here I got some error:
Cannot read property 'name' of undefined
Could anyone help me, please?
import React, {useState, useEffect} from 'react';
import FriendProfile from './FriendProfile';
import Button from './Button';
const Friend = () => {
const [friend, setFriend] = useState({})
async function getFriend() {
try{
const response = await fetch('https://www.randomuser.me/api?results=1')
const data = await response.json()
const [item] = data.results;
setFriend(item)
}catch(err){
console.log(err)
}
}
useEffect(() => {
getFriend()
}, [])
return (
<div>
<Button getFriend={getFriend} />
<FriendProfile />
</div>
);
};
export default Friend;
and FriendProfile component is:
import React from 'react';
const FriendProfile = ({friend}) => {
return (
<ul>
<li>{friend.name.first}</li>
</ul>
);
};
export default FriendProfile;
You forgot to pass to <FriendProfile /> component the friend object. In the child component it tries to destructure like ({friend}) but there is no value there. That's why you are getting that error message.
Try to pass as the following:
<FriendProfile friend={friend} />
Then in the <FriendProfile> component you need to do the additional conditional rendering:
<ul>
{
friend.name &&
<li>{friend.name.first}</li>
}
</ul>
Technically we are checking if friend.name has value or not. Once the API call returned and updated the state, it will render the <li> element with the first name of the friend object.
I hope this helps!
Related
I want to send the login user object to the profile page to edit and upload an image but It ends with the error "ProfileImage.jsx:25 Uncaught TypeError: Cannot read properties of undefined (reading 'photoUrl')"
Here is my Parent Code :
import React from 'react';
import { useState } from 'react';
import {getCurrentUser, logout} from '../../services/authService'
import {useNavigate} from "react-router-dom";
import UpdateForm from '../../components/UpdateForm';
import ProfileImage from '../../components/ProfileImage';
import './Profile.css'
import { useEffect } from 'react';
const ProfilePage = () => {
const navigate = useNavigate();
const [user,setUser] = useState();
useEffect(()=>{
const getUser = async () => {
const {data} = await getCurrentUser();
setUser(data);
}
getUser()
},[])
const handelSignOut =()=>{
logout();
setUser('');
navigate('/sign')
}
return (
<div className='prof-con'>
<div className='prof-flex-wraper'>
<div className='prof-second-row'>
<div className='prof-text'>
<ProfileImage user={user}/>
</div>
<div className='prof-form'>
<UpdateForm user={user}/>
</div>
</div>
</div>
</div>
);
}
export default ProfilePage;
and here is the profile Child component
import React from 'react';
import { useState } from 'react';
import {updatePhoto} from '../services/authService'
import DefaultUserPic from "../images/team-male.jpg";
function ProfileImage({user}) {
const [previewSrc,setPreviewSrc] = useState(null)
const [photoSrc,setPhotoSrc] = useState(null)
const handelPreview = (e)=>{
setPreviewSrc(URL.createObjectURL(e.target.files[0]))
setPhotoSrc(e.target.files[0]);
}
const handelUpload = ()=>{
const formData = new FormData();
console.log(photoSrc)
formData.append("file", photoSrc);
updatePhoto(formData);
}
//console.log(user.photoUrl)
console.log(user)
// if(previewSrc){
// var imagestr=previewSrc;
// console.log(imagestr)
// // imagestr = imagestr.replace("public/", "");
// // var profilePic="http://localhost:3001/"+imagestr;
// }else{
// var profilePic='DefaultUserPic';
// }
return (
<div className='profWraper'>
<input type="file" name="fileToUpload" id="fileToUpload" onChange={handelPreview}/>
<img className='profPhoto' src={previewSrc} alt="No-Image" />
{photoSrc && <button className='uploadBtn' onClick={handelUpload}>Upload</button>}
</div>
);
}
export default ProfileImage;
if I log the user itself I get it in the console but if I try to get any property get an error undefined.
Changing
<ProfileImage user={user}/>
to
{user && <ProfileImage user={user}/>}
might help
You are asynchronously assigning user within the useEffect of ProfileImage. That means user will be undefined until that asynchronously logic resolves. You either have to check to see if user is defined before accessing its properties, or you need to assign user some kind of default properties in your useState.
The error message suggests that user is undefined. Considering that the parent performs an async operation to get the user, I'd say the problem is that the child tries to access the user before the async operation is resolved.
Solution, add a guard against undefined user in the child:
if (user === undefined) return
I am trying to pull information from one component's API call to then use that data in another API call in a separate component. However, I am unsure how to export and use the data from the first API call in the second component.
App.js
import './App.css';
import FetchMatch from './fetch-match/fetch.match';
import FetchPlayer from './fetch-player/fetch.player';
function App() {
return (
<div className="App">
<h1>Hello world</h1>
<FetchPlayer></FetchPlayer>
<FetchMatch></FetchMatch>
</div>
);
}
export default App;
fetch.player then makes the first API call to get a users specific ID which will be used in the second API call too fetch that users match history.
fetch.player.js
import React, { useEffect, useState } from 'react';
import axios from 'axios';
const FetchPlayer = () => {
const [playerData, setPlayerData] = useState([]);
const userName = 'users name';
const userTagLine = '1234';
const apiKey = '???';
useEffect( () => {
axios.get(`https://americas.api.riotgames.com/riot/account/v1/accounts/by-riot-id/${userName}/${userTagLine}?api_key=${apiKey}`)
.then(response => {
console.log(response.data)
setPlayerData([response.data])
})
.catch(error => console.log(error))
}, []);
return (
<div>
{playerData.map( data => (
<div>
<p>{data.puuid}</p>
<p>{data.gameName}#{data.tagLine}</p>
</div>
))}
</div>
)
}
export default FetchPlayer;
not much here but just in case...
fetch.match.js
import React, { useState } from 'react';
// Somehow take in the puuid set in the state of fetch.player to make a second API call below
const FetchMatch = () => {
const [matchData, setMatchData] = useState([]);
return (
<div>
// players match list goes here
</div>
)
}
export default FetchMatch;
I am unsure if I should make a separate function instead which would allow me to create consts to handle both API calls in a single file. Or if there is a way to pass the state from fetch.player as a prop to fetch.match from App.js. I have tried to do the former but it either doesn't work or I am messing up the syntax (most likely this)
If you render both component parallelly in a parent component, they are called sibling components.
Data sharing in sibling components can be done by multiple ways (Redux, Context etc) but the easiest and simplest way (the most basic way without 3rd party API) involves the use of parent as a middle component.
First you create the state in the parent component and provide it as props to the child component which need the data from its sibling (in your case is FetchMatch).
import React from 'react';
import './App.css';
import FetchMatch from './fetch-match/fetch.match';
import FetchPlayer from './fetch-player/fetch.player';
function App() {
const [data,setData] = React.useState();
return (
<div className="App">
<h1>Hello world</h1>
<FetchPlayer></FetchPlayer>
<FetchMatch data={data} ></FetchMatch>
</div>
);
}
export default App;
Provide the function to setData as a props to the child component which will fetch the initial API (in your case is FetchPlayer)
<FetchPlayer onPlayerLoad={(data) => setData(data)} />
Then, in that child component when you finish calling the API and get the result, pass that result to the onPlayerLoad function which will call the setData function with the result as parameters. It will lead to state change and re-rendering of the second FetchMatch component feeding the props data with API results.
import React, { useEffect, useState } from 'react';
import axios from 'axios';
const FetchPlayer = ({onPlayerLoad}) => {
const [playerData, setPlayerData] = useState([]);
const userName = 'users name';
const userTagLine = '1234';
const apiKey = '???';
useEffect( () => {
axios.get(`https://americas.api.riotgames.com/riot/account/v1/accounts/by-riot-id/${userName}/${userTagLine}?api_key=${apiKey}`)
.then(response => {
console.log(response.data)
setPlayerData([response.data])
onPlayerLoad(response.data)
})
.catch(error => console.log(error))
}, []);
return <></>;
Coming to FetchMatch, you will have the data in its second rendering.
import React, { useState } from 'react';
// Somehow take in the puuid set in the state of fetch.player to make a second API call below
const FetchMatch = ({data}) => {
const [matchData, setMatchData] = useState([]);
//console.log(data);
return (
<div>
// players match list goes here
</div>
)
}
export default FetchMatch;
Now, you can do whatever you want with the shared data in second component which in your case is trigger match API. 🎉
I am making a small application that obtains data, is displayed in the DOM, and chooses an item that displays the information of the chosen user, I handle all this through the state manager called UserState, where I also add the methods to display the users. And then as a component, I have UserList and UserProfile.
This is how should work, Capture 1
UserState.js
import React, {useState} from 'react';
import UserContext from './UserContext';
import axios from 'axios';
function UserState(props) {
const initialState = {
users:[],
selectedUser:null
}
const [state, setState] = useState(initialState)
const getUsers = async() =>{
const res = await axios.get("https://reqres.in/api/users")
const data = res.data.data
setState({users:data,
selectedUser:null})
}
const getProfile = async (id) => {
const res = await axios.get("https://reqres.in/api/users/"+id)
const {data} = await res.data;
console.log('Item Selected:',data)
console.log(setState({selectedUser:data}))
}
return (
<UserContext.Provider
value={{
users:state.users,
selectedUser: state.selectedUser,
getUsers,
getProfile
}}
>
{props.children}
</UserContext.Provider>
)
}
export default UserState
I export That state and its methods through the Hook useContext, the problem starts when I try to choose a user, and the console shows me the following error.
UserList.js
import React,{useContext,useEffect} from 'react'
import UserContext from "../context/User/UserContext"
function UserList(props) {
const userContext = useContext(UserContext)
useEffect(() => {
userContext.getUsers();
},[])
return (
<div>
<h1>UserList</h1>
{userContext.users.map(user=>{
return(
<a
key={user.id}
href="#!"
onClick={()=> userContext.getProfile(user.id)}
>
<img src={user.avatar} alt="" width="70"/>
<p>{user.first_name} {user.last_name}</p>
<p>{user.email}</p>
</a>)
}): null}
</div>
)
}
export default UserList
Profile.js
import React,{useContext} from 'react'
import UserContext from '../context/User/UserContext'
function Profile() {
const {selectedUser} = useContext(UserContext)
return (
<>
<h1>Profile</h1>
{selectedUser ?
(
<div>
<h1>Selected Profile</h1>
{/* <img
src={selectedUser.avatar}
alt=""
style={{width:150}}
/> */}
</div>
):(<div>
No User Selected
</div>)}
</>
)
}
export default Profile
Console Error
I tried to change the value of selectedUser but every time the console shows me that error.
In your getProfile function, you should use setState like that.
setState({...state, selectedUser:data })
If you use setState({selectedUser:data }) then users is removed from state.
It looks like it's an issue with the asynchronous portion of your code. Initially, you have no state.users object, so when you attempt to use the properties of the state.users object in the line like {userContext.users.map(user=>{... there is nothing to map, and since map uses the length property, you are getting that error. You should check first to see if that component has a userContext.users property and that the length is greater than or equal to 1 before attempting to map.
You're using the useState hook in a slightly odd way too, which confuses things a bit. Typically when using the useState hook, each element will have its own state rather than setting a single state to handle multiple elements. In this one, you'd set two separate states, one called users and one called selectedUser and set them independently. Otherwise you can have some odd re-renders.
By the way, React error codes are very descriptive. It tells you that state.users is undefined, that it can't access property map of undefined, and that it's on line 13 of your UserList.js component. All of which is true.
Weather.JS File
import { useEffect, useState } from "react"
import axios from 'axios'
import WeatherDisplay from './WeatherDisplay'
const Weather = ({capital, params}) => {
const [weather,setWeather] = useState([])
useEffect(async () => {
const result = await axios.get('http://api.weatherstack.com/current', {params})
console.log(result.data)
setWeather(result.data)
},
[params])
return(
<div>
<h2>Weather in {capital}</h2>
<WeatherDisplay current={weather.current}/>
</div>
)
}
export default Weather
WeatherDisplay.js File
const WeatherDisplay = ({weather}) => {
console.log(weather.current.temperature)
return (
<h1>{weather.current.temperature}</h1>
)
}
export default WeatherDisplay
Having issues display the data when i use {weather.current.temperature}, it keeps giving me an error pointed at temperuture saying it isnt defined but its apart of the data
You are passing weather.current as props. While the child component is expecting weather as prop. So, what you end up doing is weather.current.current.temperature which is undefined because it doesn't exist. Just pass weather to the child prop.
Make this change when calling your child component.
<WeatherDisplay weather={weather}/>
I've retrieved datas from my database to the Recipes component. now, I'm trying to pass those datas into the RecipeList component. However, I got just the bullet points into the RecipeList component and for some reason the Recipes component return only the element " title = {recipe.title} "
I'm just starting out programming. I would be pleased if someone can help me.
Recipes
import React, {useState, useEffect} from 'react'
import axios from 'axios'
import RecipeList from '../pages/espaceUser/RecipeList'
export default function Recipes(props) {
const [recipes, setRecipes] = useState([])
const [test] = useState("text")
useEffect(() => {
const id = props.match.params.id
const getRecipes = async () => {
const url = `http://localhost:8000/user/recipes/${id}`
const result = await axios.get(url)
setRecipes(result.data)
console.log('test', result.data);
}
getRecipes()
},[])
return (
<div>
{recipes.map(recipe => (
<RecipeList
key={recipe.id}
title={recipe.title}
materiel={recipe.materiel}
ingredient={recipe.ingredient}/>
))}
</div>
)
}
RecipeList
import React from 'react'
export default function RecipeList(props) {
return (
<div>
<ul>
<li>{props.title}</li>
<li>{props.materiel}</li>
<li>{props.ingredient}</li>
</ul>
</div>
)
}
It seems to me that it is not a problem with the syntax or structure of your code, if it's rendering the title at least it means that everything is working fine. I would suggest looking at react dev tools in chrome and check out if the components have all the props they are supposed to have