How do I get the value from a React Img component - javascript

I keep getting undefined from the console.log in 'handleClickVideo'. How can I get the value out of clicking on a video properly? I tried with a div also however div doesn't have a value property. I thought Img did though.
const Videos = ({ videos }) => {
const handleClickVideo = (event) => {
console.log(event.target.value)
}
return (
<>
<h2 className="title is-5">Videos</h2>
<div className="columns is-multiline">
<VideoModal
videoOpen={videoOpen}
setVideoClose={handleClickVideo}
/>
{
videos.map((video, key) => {
return (
<div className="column is-one-third">
<div className={styles.thumbnail}>
<Img src={`https://img.youtube.com/vi/${video.link}/0.jpg`} onClick={handleClickVideo} value={video.link}/>
</div>
<p className={styles.videoTitle}>Green Book Official Trailer</p>
</div>
)
})
}
</div>
</>
)
}

You're calling handleClickVideo in VideoModal but VideoModal doesn't have any value, so it will be undefined in your callback
<VideoModal
videoOpen={videoOpen}
setVideoClose={handleClickVideo}
/>
You can make your callback function to accept a value:
const handleClickVideo = (video) => {
console.log(video)
}
And then update your render function:
<VideoModal
videoOpen={videoOpen}
setVideoClose={() => handleClickVideo(0)}
/>
<Img
src={`https://img.youtube.com/vi/${video.link}/0.jpg`}
onClick={()=>handleClickVideo(video.link)}
/>

Related

How can I map different indexes of an array on button click React

I have been trying to find a way so that when I click a button the next movie in the array shows up on the screen. I am new to react so please forgive my code. I think my problem is in how I am fetching data from the external site. I am not sure how/when I should load in the data so that it functions most effeciently. Any help or tips would be greatly appreciated
function App() {
const [items,setItems] = useState([]);
async function getItems() {
const response = await fetch('https://etbd.tech/nuspljr_334360/csv2json.php');
const data = await response.json();
setItems([...data]);
}
useEffect(() => {
getItems();
},[]);
async function loadData() {
const response = await fetch('https://etbd.tech/nuspljr_334360/sread.php?f=imdb_top_1000.csv');
const menu = await response.json();
setItems([...menu]);
}
useEffect(() => {
loadData();
},[]);
function Buttons() {
return (
<div>
<button className="button" onClick={nextMovie}>Next Movie</button>
</div>
)
}
function RenderItem() {
return (
<div>
{items.map((item) => (
<div key={v4()} className='card'>
<section className="description">
<img src={item.Poster_Link} alt="Poster_Image"/>
<section className="title">
<p>{item.Series_Title} ({item.Released_Year})</p>
<p>IMDB Rating: {item.IMDB_Rating}</p>
</section>
<p>{item.Overview}</p>
<p>{item.Genre} - {item.Runtime}</p>
</section>
</div>
))}
</div>
)};
return (
<div className="body">
<h2>Guess that Movie</h2>
<div className="table">
<RenderItem />
<div className="nav">
<Buttons />
</div>
</div>
</div>
);
}
export default App;
Okay, some things missing in your code.
First of all, you are referencing to nextMovie function and did not declare it.
After this, you should think about the logic of your component and how it should behave.
You do have a state for storing your items that you are fetching.
Now you need another state to store some kind of logic to display the current movie and when you click the next movie button, to update this state with the next one.
You can set a const [activeIndex, setActiveIndex] = useState(-1);
When you first fetch your items, you can set movieIndex to 0.
And inside your component render item, you will get rid of the items.map; because you don't want to loop inside your array, you just want to show one.
We are now passing the active movie as a prop to RenderItem component and showing the active movie data.
I recommend that you learn more about javascript before trying react code.
You can see a working demo here: https://codesandbox.io/s/optimistic-sid-8morgf?file=/src/App.js
import React, {useState, useEffect} from 'react'
function App() {
const [items,setItems] = useState([]);
const [activeIndex,setActiveIndex] = useState(-1);
async function getItems() {
const response = await fetch('https://etbd.tech/nuspljr_334360/csv2json.php');
const data = await response.json();
setActiveIndex(0);
setItems([...data]);
}
useEffect(() => {
getItems();
},[]);
function Buttons() {
return (
<div>
<button className="button" onClick={nextMovie}>Next Movie - next index {activeIndex + 1}</button>
</div>
)
}
function RenderItem({item}) {
return (
<div>
<div className='card'>
<section className="description">
<img src={item.Poster_Link} alt="Poster_Image" />
<section className="title">
<p>{item.Series_Title} ({item.Released_Year})</p>
<p>IMDB Rating: {item.IMDB_Rating}</p>
</section>
<p>{item.Overview}</p>
<p>{item.Genre} - {item.Runtime}</p>
</section>
</div>
</div>
)};
const nextMovie = () => setActiveIndex((prev) => prev + 1);
return (
<div className="body">
<h2>Guess that Movie</h2>
<div className="table">
{items.length === 0 ? 'Loading...' : (<>
<RenderItem item={items[activeIndex]} />
<div className="nav">
<Buttons />
</div>
</>)}
</div>
</div>
);
}
export default App;

Multiple Buttons with their own Events in ReactJS

What would be the best way to handle multiple buttons that show/hide their respective DIVs.
Example of the Code I have below. As you can see it's set to handle just the first button. I need a method to continually add more buttons that show/hide their own DIVs.
Example: When Button(A) is clicked DIV(A) fades in. If Button(A) is clicked again DIV(A) fades out. If/instead Button(B) is clicked and DIV(A) is visible, then DIV(A) fades out and DIV(B) fades in. so on.. etc..
const Example2: React.FC = () => {
const [style, setStyle] = useState("invis");
const changeStyle = () => {
console.log("you just clicked");
setStyle("vis");
};
return (
<Container className="content">
<section className="section">
<div className="video_section">
<video src={videoUrl} width="" height ="" loop autoPlay muted> </video>
<div className="video_words">
<p><span>Video Words</span></p>
<h1>
<span>V</span>
<span>I</span>
<span>D</span>
<span>E</span>
<span>O</span>
</h1>
<div className="TierTwo">
<div className="Top1">
<button type="button" onClick={changeStyle} className="Lrow1">
<img src="/img/image1.png" height="auto" width="auto" />
</button>
<div className={style}>
<One />
</div>
<div className={style}>
<Two />
</div>
<div className="Rrow1">
<button type="button" onClick={changeStyle} className="Rrow1">
<img src="/img/image2.png" height="auto" width="auto" />
</button>
</div>
</div>
<div className="Top2">
<button type="button" onClick={changeStyle} className="Lrow2">
<img src="/img/image3.png" height="auto" width="auto" />
</button>
<div className={style}>
<Three />
</div>
<div className={style}>
<Four />
</div>
<div className="Rrow1">
<button type="button" onClick={changeStyle} className="Rrow2">
<img src="/img/image4.png" height="auto" width="auto" />
</button>
</div>
</div>
<div className="Top2">
..etc..
As we want there only exists only one state that manage multiple buttons, such that no manual useState declaration, thus, we consider there is only a single state style, and it is an object, the key of style would be the button name or index, and by using changeStyle(index)() should trigger the onclick logic for button[index], for achieving the pattern like below:
(
<button onClick={changeStyle(1)}>button1</button>
<button onClick={changeStyle(2)}>button2</button>
<button onClick={changeStyle(3)}>button3</button>
<div>{style[1]}</div>
<div>{style[2]}</div>
<div>{style[3]}</div>
)
Since we want changeStyle(index) would by default set the state style[index] to "invis".
Hence will suppose the changeStyle function will be look
const changeStyle = (index) => {
// set style[index] to "invis" by function setStyle
// onclick function goes here
return (event) => {
// this block will be executed on every onclick
}
}
And since we leveraged the setStyle function inside a IIFE to assign default value of each index,
therefore, once we successfully set, we should design a mechanism that would stop constantly trigger it again, or else a forever render-loop will be happened.
const changeStyle = (index) => {
if (!style[index]) {
// setStyle goes here
}
// ...
}
And in order to improve atomicity of each setStyle operation, we should pass a callback function to the setStyle function instead of a value.
const changeStyle = (index) => {
if (...) {
setStyle((style) => {
...style,
[index]: "invis",
});
}
// ...
}
The handy function is done, now considering the onclick logic.
Regarding the logic of showing and hiding:
apply all style to invis regardless of their current states
toggle current style[index]: if it is "invis", then set it to "vis", vice versa.
hence, the setStyle function in onclick function should looks like:
setStyle((style) => ({
// set all style to "invis"
...(
Object.keys(style)
.reduce((a, b) => ({
...a,
[b]: "invis"
}), {})
),
// toggle current index state
[index]: style[index] === "invis" ? "vis" : "invis",
}));
Complete solution:
const { useState } = React;
function App() {
// the type of style will be { [key: any]: "invis" | "vis" }
const [style, setStyle] = useState({});
// IIFE for filling default value "invis"
// return a callback when onclick happened
const changeStyle = (index) => {
// this will be run for the amount of button you have
if (!style[index]) {
setStyle((style) => ({
...style,
[index]: "invis",
}));
}
// for every onclick, this function will be run
return () => {
setStyle((style) => ({
...(
Object.keys(style)
.reduce((a, b) => ({
...a,
[b]: "invis"
}), {})
),
[index]: style[index] === "invis" ? "vis" : "invis",
}));
};
};
return (
<div className="App">
<button onClick={changeStyle(1)}>button1</button>
<button onClick={changeStyle(2)}>button2</button>
<button onClick={changeStyle(3)}>button3</button>
<div>{style[1]}</div>
<div>{style[2]}</div>
<div>{style[3]}</div>
</div>
);
}
ReactDOM.render(
<App />,
document.getElementById("root")
);
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/16.7.0-alpha.0/umd/react.production.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/16.7.0-alpha.0/umd/react-dom.production.min.js"></script>
<div id="root"></div>

useState hook is breaking activePost as useEffect is triggered by selectedPost

My goal was to fetch posts from Graphcms and populate an array - posts, and populate it into postlist, then the main component will change according to what the user clicks on a post from postlist, I can see the posts array is populated , but when i click on a post on postlist I get the following error
Main.js:22 Uncaught TypeError: Cannot read properties of undefined (reading 'featuredImage')
Below my files
App.js
function App() {
const [selectedPost,setSelectedPost] = useState(0);
const [posts, setPosts] = useState([]);
useEffect(() => {
const fetchPosts = async () => {
const { posts } = await request(
'https://api-ap-southeast-2.graphcms.com/v2/ckxo1np9m5kw601xpccps4lrn/master',
`
{
posts {
id
title
slug
excerpt
featuredImage
{
url
}
}
}
`
);
console.log("print posts " , posts)
setPosts(posts);
};
fetchPosts();
}, []);
return ( <div className='app'>
<Header/>
{
posts.length>0 && (<>
<Main posts={posts} selectedPost={selectedPost}/>
<PostList posts={posts} setSelectedPost={setSelectedPost} />
</>
)
}
</div>
)
}
export default App;
And the Main.js Component
const Main = ({selectedPost,posts}) => {
const[activePost,setActivePost] =useState(posts[0])
console.log("activePost ", activePost)
useEffect(()=>{
setActivePost(posts[selectedPost])
},[posts,selectedPost])
return (
<div className='main'>
<div className='mainContent'>
<div className='postHighlight'>
<div className='postContainer'>
<img
className='selectedPost'
src= {activePost.featuredImage.url}
alt=''
/>
</div>
</div>
<div className='postDetails' style={{color:'#fff'}}>
<div className='title'>
{activePost.title} </div>
<span className='itemNumber'></span>
<span className='postExcerpt'>{activePost.excerpt}</span>
</div>
<div className='otherDetails'>
</div>
</div>
</div>
)
}
export default Main
And then we have postList.js file
const PostList = ({posts,setSelectedPost}) => {
return (
<div className='postList'>
{posts.map(post=>(
<div onClick={()=>setSelectedPost(post.id)}>
<CollectionCard key={post.slug} title={post.title} excerpt={post.excerpt} imageSrc={post.featuredImage.url}/>
</div>
)) })
</div>
)
}
export default PostList
Based on your app, you are using the index of the selected post.
The error arises from your onclick function. You are passing post.id to setSelectedPost() so you are accessing the posts array incorrectly. Hence, the undefined.
Just use the current index on your map function:
<div className='postList'>
{posts.map((post, index) => (
<div onClick={() => setSelectedPost(index)} key={post.slug}>
<CollectionCard
title={post.title}
excerpt={post.excerpt}
imageSrc={post.featuredImage.url}
/>
</div>
))
}
</div>

Redirect to description page from Card component in Reactjs

Appreciate your kind help!
What I'm trying to achieve is that on click of cardlist component it has to open respective product detail page just like e-commerce website.
I have created :
CardList component, card component, data(where it contain array of
object) and singleCardComponent(i.e the description page component)
I think I had made a mistake in cardList component.
I have got stuck on the logic how i will redirect to respective product page.
//--------------------card component--------------------
class Card extends Component {
render() {
return (
<div className='col-md-3 col-10 mx-auto mb-4'>
<div className="card">
<img src={this.props.imgsrc} className="card-img-top" alt="..." />
<div className="card-body">
<h5 className="card-title">Rs {this.props.price}</h5>
<p className="card-text">{this.props.title}</p>
<p className='card-date d-flex justify-content-end'>Oct 29</p>
</div>
</div>
</div>
)
}
}
//--------------------cardlist component--------------------
class CardList extends Component {
show_component = (i) => {
Data.map((v) => {
return <SingleCardComp
key={i}
imgsrc={v.imgsrc}
price={v.price}
title={v.title}
seller={v.seller_desc}
desc={v.description}
loc={v.location}
/>
})
}
render() {
return (
<div className='row'>
{
Data.map((val, i) => {
return <button onClick={()=>this.show_component(i)}>
<Card
key={i}
imgsrc={val.imgsrc}
price={val.price}
title={val.title}
/>
</button>
})
}
</div>
)
}
}
//--------------------Data--------------------
const Data = [
{
imgsrc: image0,
title: "Samsung A50",
price: 35500,
seller_desc: 'Bilal',
description: "Lorem, ipsum dolor sit",
location:'Kansas'
}
];
//--------------------SingleCardComp--------------------
class SingleCardComp extends Component {
render() {
return (
<div>
<img src={this.props.imgsrc} alt="..." />
<h5>Rs {this.props.price}</h5>
<p >{this.props.title}</p>
<h1>
Description:{this.props.desc}
</h1>
<h1>
Seller Details:{this.props.seller}
</h1>
<h1>
Posted in:{this.props.loc}
</h1>
</div>
)
}
}
Here is the image of card
The show_component method seems the problem here,
What is the Data means in the show_component method? Is that the same array used in CardList->render method? If that is the case what you need to do is update your show_component method like this,
show_component = (i) => {
Data.map((v, index) => {
if(index === i) {
return <SingleCardComp
key={i}
imgsrc={v.imgsrc}
price={v.price}
title={v.title}
seller={v.seller_desc}
desc={v.description}
loc={v.location}
/>
}
})
}
This is the solution with minimum change. However, the better solution would be to pass the card data in the show_component method as parameter. Like this,
state = {
selectedItem: null,
showSingleCardComp: false,
selectedItemIndex: 0,
}
show_component = (v, i) => {
this.setState({selectedItem: v, showSingleCardComp: true, selectedItemIndex: i});
}
And call the show_component method like this,
render() {
return (
<div className='row'>
{
Data.map((val, i) => {
return <button onClick={()=>this.show_component(val, i)}>
<Card
key={i}
imgsrc={val.imgsrc}
price={val.price}
title={val.title}
/>
</button>
})
}
{this.state.showSingleCardComp && (
<SingleCardComp
key={this.state.selectedItemIndex}
imgsrc={this.state.selectedItem.imgsrc}
price={this.state.selectedItem.price}
title={this.state.selectedItem.title}
seller={this.state.selectedItem.seller_desc}
desc={this.state.selectedItem.description}
loc={this.state.selectedItem.location}
/>
)}
</div>
)
}

State has filled with objects but cant access in return

my 'pokemonData' state has filled with data i need but, i cant display them on screen, it just gives an empty white screen. What am I missing ?
Here is my state variables;
const [pokemonData, setPokemonData] = useState([]);
const initialUrl = 'https://pokeapi.co/api/v2/pokemon/';
The whole useEffect is used for first getting a data from 'fetchData', returns a needed list of URLs which i have to fetch again in 'loadingPokemon' then i set my pokemonData state.
useEffect(() => {
const fetchData = async () => {
setLoading(true);
let response = await axios.get(initialUrl);
setNextUrl(response.next);
setPrevUrl(response.previous);
await loadingPokemon(response.data.results);
setLoading(false);
};
const loadingPokemon = async (datas) => {
let pokemonRecords = [];
let pokemonDatas = [];
datas.map((data) => {
pokemonRecords.push(data.url);
});
pokemonRecords.forEach(async (record) => {
let pokemonDatasDatas = await axios.get(record);
pokemonDatas.push(pokemonDatasDatas.data);
});
setPokemonData(pokemonDatas);
console.log(pokemonData);
};
fetchData();
}, []);
console.log(pokemonData);
Also here is my render return;
return (
<div className='App'>
{pokemonData.map((pokemon, index) => (
<div className='Card'>
<div className='Card__img'>
<img src={pokemon.sprites.front_default} alt='' />
</div>
<div className='Card__Name'>{pokemon.name}</div>
<div className='Card__types'>
{pokemon.types.map((type) => {
return <div className='Card__type'>{type.type.name}</div>;
})}
</div>
<div className='Card__info'>
<div className='Card__data Card__data--weight'>
<p className='title'> Weight </p>
<p> {pokemon.weight} </p>
</div>
<div className='Card__data Card__data--height'>
<p className='title'> Height </p>
<p> {pokemon.height} </p>
</div>
<div className='Card__data Card__data--ability'>
<p className='title'> Abilities </p>
<p> {pokemon.abilities[0].ability.name} </p>
</div>
</div>
</div>
))}
</div>
);
Try conditional rendering. I see that you are tracking your loading state, so you could use that - if data is not loading (pokemons are fetched), then list your pokemons.

Categories