Only one prop is able to pass the data - reactjs

When I try to pass props from one component to another, only one of them receives data. I am not able to get "users" details to AccountabilityChart component.
I am only able to get data props but not users props. What is the issue? I am not able to figure it out the behaviour. Can any one explain why this happens and if their is a way to have both the props
const Card = (props: any) => {
useEffect(()=>{
console.log("These are props: ", props);
}, []);
const [selectedOption, setSelectedOption] = useState(null);
return (
<ul>
{props.data.map((item: any, index: any) => (
<React.Fragment key={item.name}>
<li>
<div className="card">
<div className="card-body">
<p>{item.name}</p>
</div>
<div>
<Select
defaultValue={selectedOption}
onChange={handleChange}
className="select"
options={props.users}
/>
</div>
<div></div>
</div>
{item.children?.length && <Card data={item.children} users={[]} />}
</li>
</React.Fragment>
))}
</ul>
);
};
const AccountabilityChartComponent = () => {
const [hierarchy, setHierarchy] = useState([]);
const [users, setUsers] = useState([]);
useEffect(() => {
someApiCall.then(
function success(results) {
setHierarchy(results.entities);
}
);
someApiCall.then(
function success(results: any) {
setUsers(wrapped);
}
);
}, []);
return (
<div className="grid">
<div className="org-tree">
<Card
users={users}
data={hierarchy}
/>
</div>
</div>
);
};
export default AccountabilityChartComponent;
Organizational hierarchy I am trying to create
Console log is called 4 times. It means the state is updated 4 times? Will it effect my performance?

Related

How to get specific data from api with condition

Hello so i tried to make an website using Goole Books API. I want to get the listPrice from the object, but theres some of the book that doesnt have the listPrice in them. So for the example in object number 1 there is some code called saleability: "NOT_FOR_SALE" meanwhile object number 2 have and saleability: "FOR_SALE". If i tried to map the data, there is a error Uncaught TypeError: i.saleInfo.saleability.listPrice is undefined. How do you make spesific condition for this problem.
This is the code :
const CardBooks = (props) => {
const url = "https://www.googleapis.com/books/v1/volumes?q=:keyes&key=AIzaSyDIwDev4gFHRqCh4SSaO9eLKEeI7oYt6aE&maxResults=27"
const result = "&maxResults=40"
const [bookHome, setBookHome] = useState([]);
const [modalShow, setModalShow] = React.useState(false);
useEffect( () => {
axios
.get(`${url}`)
.then( (res) => {
setBookHome(res?.data?.items)
console.log(res?.data?.items)
})
.catch(console.error);
}, [])
return (
<div>
<Container fluid className='wrapper'>
{bookHome && bookHome.map((i, index) => {
return(
<div className='image-container' key={index}>
<div className="book read">
<div className="cover">
<img src={i.volumeInfo.imageLinks.thumbnail} />
</div>
<div className="info">
<h3 className="title">{i.volumeInfo.title}</h3>
</div>
<Example
thumbnail={i.volumeInfo.imageLinks.thumbnail}
title={i.volumeInfo.title}
description={i.volumeInfo.description}
category={i.volumeInfo.categories}
page={i.volumeInfo.pageCount}
language={i.volumeInfo.language}
publisher={i.volumeInfo.publisher}
published={i.volumeInfo.publishedDate}
link={i.volumeInfo.previewLink}
epub={i.accessInfo.epub.isAvailable}
currency={i.saleInfo.saleability.listPrice.currencyCode}
price={i.saleInfo.saleability.listPrice.amount}
/>
</div>
</div>
)
})}
</Container>
</div>
)
}
export default CardBooks
Basically you just need a null/undefined check, a quick and dirty solution:
currency={i.saleInfo.saleability.listPrice ? i.saleInfo.saleability.listPrice.currencyCode : ''}
It's better to use conditional rendering and/or passing the whole object to the component and handling it inside.

State returning undefined in typescript

I'm trying to change the state of a component with data from an external api. The state is to add a component as a favourite, but returns undefined. I'm already using arrow functions and the .bind() didn't work as well. What am I missing here?
Library Component:
export default function LibraryComponent() {
const [albums, setAlbums] = useState<any[]>([]);
const [albumTitle, setAlbumTitle] = useState<any[]>([]);
const [photoUrl, setPhotoUrl] = useState();
const [favourite, setFavourite] = useState<any[]>([]);
//FETCH DATA
const fetchData = () => {
const getAlbums = "https://jsonplaceholder.typicode.com/albums?_limit=20";
const getPhotos =
"https://627ed423b75a25d3f3bd811f.mockapi.io/api/photos/1"; //random number here
const albums = axios.get(getAlbums).then((res) => {
setAlbums(res.data);
});
const photoUrl = axios.get(getPhotos).then((res) => {
setPhotoUrl(res.data.images);
});
};
const addFavourite = (album: []) => {
const favouriteList = [...favourite, album];
setFavourite(favouriteList);
};
return (
<>
<div className="container-fluid w-50">
<div className="row">
{albums.map((album) => (
<div key={album.id} className="col-lg-2 col-sm-6">
<div className="col-lg-12 col-sm-6">
<div className="thumbnail img-responsive">
<AlbumComponent
title={album.title}
image={photoUrl}
handleFavouriteClick={addFavourite}
/>
</div>
</div>
</div>
))}
</div>
</div>
</>
);
}
Album Component:
type Meta = {
title: any;
image: any;
handleFavouriteClick: any;
};
export default function AlbumComponent(props: Meta) {
return (
<>
<img src={props.image} alt="" className="img-fluid img-thumbnail" />
<p>{props.title}</p>
<div onClick={() => props.handleFavouriteClick()}>
<i className="fa-regular fa-heart"></i>
</div>
</>
);
}
addFavourite expects an album parameter.
Try something like this:
<AlbumComponent
title={album.title}
image={photoUrl}
handleFavouriteClick={() => addFavourite(album)}
/>
You need to pull the value from the input and provide it as a parameter to the function. You could use a controlled input for this. Somehting like this:
export default function AlbumComponent(props: Meta) {
const [value, setValue] = React.useState()
return (
<>
<img src={props.image} alt="" className="img-fluid img-thumbnail" />
<p>{props.title}</p>
<div onClick={() => props.handleFavouriteClick(value)}>
<i className="fa-regular fa-heart" value={value} onChange={setValue}></i>
</div>
</>
);
}

Use State not updating as expected

Fairly new to react and trying to build a clone of The Movie Database site. I want this toggle switch to change my api call from movies to tv. It starts working after a couple clicks, but then it throws everything off and it's not displaying the correct items anyway. Not really sure what's going on here...or even why it starts working after two clicks. Anyone know whats up with this?
import React, { useState, useEffect } from "react";
import axios from "axios";
import API_KEY from "../../config";
const Popular = ({ imageUri }) => {
// GET POPULAR MOVIES
const [popularMovies, setPopularMovies] = useState("");
const [genre, setGenre] = useState("movie");
console.log(genre);
const getPopular = async () => {
const response = await axios.get(
`https://api.themoviedb.org/3/discover/${genre}?sort_by=popularity.desc&api_key=${API_KEY}`
);
setPopularMovies(response.data.results);
};
useEffect(() => {
getPopular();
}, []);
const listOptions = document.querySelectorAll(".switch--option");
const background = document.querySelector(".background");
const changeOption = (el) => {
let getGenre = el.target.dataset.genre;
setGenre(getGenre);
getPopular();
listOptions.forEach((option) => {
option.classList.remove("selected");
});
el = el.target.parentElement.parentElement;
let getStartingLeft = Math.floor(
listOptions[0].getBoundingClientRect().left
);
let getLeft = Math.floor(el.getBoundingClientRect().left);
let getWidth = Math.floor(el.getBoundingClientRect().width);
let leftPos = getLeft - getStartingLeft;
background.setAttribute(
"style",
`left: ${leftPos}px; width: ${getWidth}px`
);
el.classList.add("selected");
};
return (
<section className="container movie-list">
<div className="flex">
<div className="movie-list__header">
<h3>What's Popular</h3>
</div>
<div className="switch flex">
<div className="switch--option selected">
<h3>
<a
data-genre="movie"
onClick={(e) => changeOption(e)}
className="switch--anchor"
>
In Theaters
</a>
</h3>
<div className="background"></div>
</div>
<div className="switch--option">
<h3>
<a
data-genre="tv"
onClick={(e) => changeOption(e)}
className="switch--anchor"
>
On TV
</a>
</h3>
</div>
</div>
</div>
<div className="scroller">
<div className="flex flex--justify-center">
<div className="flex flex--nowrap container u-overScroll">
{popularMovies &&
popularMovies.map((movie, idX) => (
<div key={idX} className="card">
<div className="image">
<img src={imageUri + "w500" + movie.poster_path} />
</div>
<p>{movie.title}</p>
</div>
))}
</div>
</div>
</div>
</section>
);
};
export default Popular;
You're using the array index as your key prop when you're mapping your array.
You should use an id that is specific to the data that you're rendering.
React uses the key prop to know which items have changed since the last render.
In your case you should use the movie id in your key prop instead of the array index.
popularMovies.map((movie) => (
<div key={movie.id} className="card">
<div className="image">
<img src={imageUri + 'w500' + movie.poster_path} />
</div>
<p>{movie.title}</p>
</div>
));
Also
You're calling the api directly after setGenre. However state changes aren't immediate. So when you're making your api call you're still sending the last movie genre.
Two ways of fixing this:
You could call your function with the genre directly, and change your function so it handles this case:
getPopular('movie');
Or you could not call the function at all and add genre as a dependency of your useEffect. That way the useEffect will run each time the genre change.
useEffect(() => {
getPopular();
}, [genre]);
PS: You should consider splitting your code into more component and not interacting with the DOM directly.
To give you an idea of what it could look like, I refactored a bit, but more improvements could be made:
const Popular = ({ imageUri }) => {
const [popularMovies, setPopularMovies] = useState('');
const [genre, setGenre] = useState('movie');
const getPopular = async (movieGenre) => {
const response = await axios.get(
`https://api.themoviedb.org/3/discover/${movieGenre}?sort_by=popularity.desc&api_key=${API_KEY}`
);
setPopularMovies(response.data.results);
};
useEffect(() => {
getPopular();
}, [genre]);
const changeHandler = (el) => {
let getGenre = el.target.dataset.genre;
setGenre(getGenre);
};
const isMovieSelected = genre === 'movie';
const isTvSelected = genre === 'tv';
return (
<section className="container movie-list">
<div className="flex">
<MovieHeader>What's Popular</MovieHeader>
<div className="switch flex">
<Toggle onChange={changeHandler} selected={isMovieSelected}>
In Theaters
</Toggle>
<Toggle onChange={changeHandler} selected={isTvSelected}>
On TV
</Toggle>
</div>
</div>
<div className="scroller">
<div className="flex flex--justify-center">
<div className="flex flex--nowrap container u-overScroll">
{popularMovies.map((movie) => {
const { title, id, poster_path } = movie;
return (
<MovieItem
title={title}
imageUri={imageUri}
key={id}
poster_path={poster_path}
/>
);
})}
</div>
</div>
</div>
</section>
);
};
export default Popular;
const Toggle = (props) => {
const { children, onChange, selected } = props;
const className = selected ? 'switch--option selected' : 'switch--option';
return (
<div className={className}>
<h3>
<a
data-genre="movie"
onClick={onChange}
className="switch--anchor"
>
{children}
</a>
</h3>
<div className="background"></div>
</div>
);
};
const MovieHeader = (props) => {
const { children } = props;
return (
<div className="movie-list__header">
<h3>{children}</h3>
</div>
);
};
const MovieItem = (props) => {
const { title, imageUri, poster_path } = props;
return (
<div key={idX} className="card">
<div className="image">
<img src={imageUri + 'w500' + poster_path} />
</div>
<p>{title}</p>
</div>
);
};

separate a page to components in react

this is one of my pages in react app and it shows everything ok:
const showAllFavorite = (props) => {
const [favorites, setFavorites] = useState([]);
useEffect(async () => {
const resp = await generalGet({}, '/favorite');
setFavorites(resp.data);
}, [])
return(
<React.Fragment>
<MainMenu title="favorites" history={props.history}/>
<div className="container mt-4">
<div className="d-flex flex-wrap">
{
favorites.map(fav => (
<div style={{flex: '50%'}}>
<Link
to={`/recipe/${fav.id}`}
key={fav.id}
>
<FoodItem
className="foodItem"
name={fav.name}
cover={fav.cover}
width={'auto'}
/>
</Link>
</div>
))
}
</div>
</div>
</React.Fragment>
)
}
export default showAllFavorite;
but when I want to code like this it doesn't show anything :
const showAllFavorite = (props) => {
const [favorites, setFavorites] = useState([]);
useEffect(async () => {
const resp = await generalGet({}, '/favorite');
setFavorites(resp.data);
}, [])
return(
<showFoodItems title={"favorite"} items={favorites} history={props.history} />
)
}
export default showAllFavorite;
and my showFoodItem component:
const showFoodItems= ({title , items , history}) => {
return(
<React.Fragment>
<MainMenu title={title} history={history}/>
<div className="container mt-4">
<div className="d-flex flex-wrap">
{
items.map(item => (
<div style={{flex: '50%'}}>
<Link
to={`/recipe/${item.id}`}
key={item.id}
>
<FoodItem
className="foodItem"
name={item.name}
cover={item.cover}
width={'auto'}
/>
</Link>
</div>
))
}
</div>
</div>
</React.Fragment>
)
}
export default showFoodItems;
I want to do this because I want to use the showFoodItems component many times in my app.
I checked the network tab and data is receiving from server. do you have any idea?
The problem is most likely with your component names.
React treats components that start with lower case letters as DOM tags so changing the names to ShowAllFavorite and ShowFoodItems should fix your issue.
You can read more about the reasoning behind this in React's documents here.

Why does React tell me unexpected token "."

I'm trying to display an json array on the screen but react tells me unexpected token "." I have searched around for 3 hours now but I can't figure out what is wrong or how to fix this. The other parts of the detail object all display correctly but for some reason the array just doesn't want to. I hope someone can help me with this problem.
the exact error I get is:
and the json in console log.
below is my code for the component.
function GeneDetail({ match }) {
useEffect(() => {
fetchDetails();
}, []);
const [detail, setDetail] = useState({})
//const [alleles, setAlleles] = useState([])
const fetchDetails = async () => {
const fetchDetails = await fetch(
'/api/get_genedetail?g='+match.params.genename+''
);
const detail = await fetchDetails.json()
setDetail(detail)
//setAlleles(detail.alleles)
}
console.log('alleles', detail.alleles)
return(
<div className="main-content">
<Container maxWidth="lg">
<div className="grid-container">
<div className="grid-title">
<h2>Gene: <i>{detail.geneName}</i></h2>
</div>
<div className="grid-subtitle">
<h3>Type: {detail.segmentFullName}</h3>
</div>
<div className="grid-alleles">
test
{detail.alleles ?
{detail.alleles.map(function (allele, i) {
return <div key={i}>
<h5>{allele.Number}</h5>
</div>
})}
: (<p>"No alleles found."</p>)}
</div>
</div>
</Container>
</div>
);
}
React errors can be confusing, the problem here is not that you have a dot there. Instead, you declare a variable expression in a variable expression, essentially like this:
{condition?{mappedData}:(alternative)}
You cannot declare an expression in an expression, you should've written it like this:
{detail.alleles ?
detail.alleles.map(function (allele, i) {
return <div key={i}>
<h5>{allele.Number}</h5>
</div>
})
: (<p>No alleles found.</p>)}
UpVote, if the solution works
function GeneDetail({ match }) {
useEffect(() => {
fetchDetails();
}, []);
const [detail, setDetail] = useState({})
//const [alleles, setAlleles] = useState([])
const fetchDetails = async () => {
const fetchDetails = await fetch(
'/api/get_genedetail?g='+match.params.genename+''
);
const detail = await fetchDetails.json()
setAlleles(detail.alleles)
}
console.log('alleles', detail.alleles)
return(
<div className="main-content">
<Container maxWidth="lg">
<div className="grid-container">
<div className="grid-title">
<h2>Gene: <i>{detail.geneName}</i></h2>
</div>
<div className="grid-subtitle">
<h3>Type: {detail.segmentFullName}</h3>
</div>
<div className="grid-alleles">
test
{alleles.length >= 0 ?
{alleles.map( (allele, i) => {
return <div key={i}>
<h5>{allele.Number}</h5>
</div>
})}
: (<p>"No alleles found."</p>)}
</div>
</div>
</Container>
</div>
);
}

Resources