I'm having issue where when i put my state into index of an array, it gives error.
Here is there line of bug : {WorkData[state].title}
What i need to do is to display the elements of WorkData which is a array with objects : title, content, img math etc...
const WorkData = [
{
id: 0,
title: 'title',
subtext: 'React/Express',
content: 'content',
imgPath: 'imgPath',
},
{
id: 1,
title: 'Little Hero Academy',
subtext: 'React/API REST',
content:
'content,
imgPath: 'imgPath',
},
{
id: 2,
title: 'title',
subtext: 'subtext',
content: 'content',
imgPath: 'imgPath',
},
First, i have a list of cards with a button that contains data:
<div className="work-container flex">
<div className="work-bloc zoom">
<div className="work-hover" />
<div className="work-text">
<div className="text-title">{WorkData[0].title}</div>
<span className="subtext">{WorkData[0].subtext}</span>
</div>
<div
onClick={togglePopup}
data-id={WorkData[0].id}
className="work-showmore button2"
>
Show More
</div>
</div>
</div>
I store the data in state with this function :
const [id, setId] = useState();
const togglePopup = (e) => {
setIsOpen(!isOpen);
setId(e.target.dataset.id);
};
I need that data to get to the popup,
<Popup
togglePopup={togglePopup}
closePopup={closePopup}
isOpen={isOpen}
id={id}
/>
I pass the state to my popup component and try to display the value of id which is displayed
Also,i need to make the popup display of the content of the array that belong to the id (index) i passed in state : workData 1 2 3 etc...
const Popup = ({ closePopup, isOpen, id }) => {
return (
<div className={`popup-container ${isOpen ? 'opened' : 'closed'}`}>
<div className={`popup ${isOpen ? 'opened2' : 'closed2'}`}>
<span className='popup-title'>
{WorkData[id].title}
{id}
</span>
<div className='image-container'>
<img
src='https://i.picsum.photos/id/1060/536/354.jpg?blur=2&hmac=0zJLs1ar00sBbW5Ahd_4zA6pgZqCVavwuHToO6VtcYY'
alt=''
/>
</div>
<p>
Team projet with React and API REST. Our goal was to make an app with
a choosen API and make something out of it. We did a superhero-themed
website with games for children.
<br />
Check on <AiFillGithub className='menuicon' />
</p>
<div onClick={closePopup} className='close-icon button2'>
Show less{' '}
</div>
</div>
</div>
);
};
but i get this error :
**TypeError: _WorkData__WEBPACK_IMPORTED_MODULE_1__.default[id] is undefined**
Thanks in advance for any suggestion
Ok i fixed, it i changed the way of doing it, i mapped my workcard components so i can easily retrieve the id, title, etc, and i didnt change the event onclick to catch the card.id in state, i passed the state to parent, then to the popup, and then into the popup component i just imported the WorkData.js and simply did : WorkData[state-with-id].title etc
See sample of code below
works.js
const togglePopup = (e) => {
setIsOpen(!isOpen);
setPopupId(e.target.dataset.id);
};
const closePopup = () => {
setIsOpen(false);
};
useEffect(() => {
setCards(workData);
}, []);
workcards.js
<div
onClick={togglePopup}
className='work-showmore button2'
data-id={workcard.id}
>
popup.js
import workData from './workData';
<span className='popup-title'>{workData[popupId].title}</span>
Related
I have a component called RightTab like this
const RightTab = ({ data }) => {
return (
<div className="RightTab flex__container " onClick={data.onClick}>
<img src={data.icon} alt="Dashboard Icon" />
<p className="p__poppins">{data.name}</p>
{data.dropDown === true ? (
<div className="dropdown__icon">
<img src={Assets.Arrow} alt="Arrow" />
</div>
) : (
<div className="nothing"></div>
)}
</div>
);
};
export default RightTab;
The tab has an active state in its CSS like this
.RightTab.active {
background-color: var(--primaryGreen);
}
as you have seen it changes the color when an active class is added. I have an array in the parent component that I pass down to the child component as props. Here is the array
const dataArray = [
{
name: "Dashboard",
icon: Assets.Dashboard,
dropDown: false,
onClick: handleDashBoardClick,
},
{
name: "Inventory",
icon: Assets.Inventory,
dropDown: true,
onClick: handleInventoryClick,
},
{
name: "Reports",
icon: Assets.Reports,
dropDown: true,
onClick: handleReportsClick,
},
];
Here is how I pass the props down.
<RightTab data={dataArray[0]} />
<RightTab data={dataArray[1]} />
<RightTab data={dataArray[2]} />
The data prop passed into the component is an object containing a function call as one of its properties like this. I have an onclick attribute on the child components' main container that is supposed to call the respective function.
The function is what adds the active class to make the background change color. However each time I click on the component it only changes the background of the first occurrence. And as you may have noticed I call the component thrice. No matter which component I click only the first ones background changes.
Here is an example of the function that is on the prop object.
const handleDashBoardClick = () => {
const element = document.querySelector(".RightTab");
element.classList.toggle("active");
};
I don't get what I'm doing wrong. What other approach can I use?
Although you use the component 3 times, it doesn't mean that a change you make in one of the components will be reflected in the other 2, unless you specifically use a state parameter that is passed to all 3 of them.
Also, the way you add the active class is not recommended since you mix react with pure js to handle the CSS class names.
I would recommend having a single click handler that toggles the active class for all n RightTab components:
const MainComponent = () => {
const [classNames, setClassNames] = useState([]);
const handleClick = (name) =>
{
const toggledActiveClass = classNames.indexOf('active') === -1
? classNames.concat(['active'])
: classNames.filter((className) => className !== 'active');
setClassNames(toggledActiveClass);
switch (name) {
case 'Dashboard';
// do something
break;
case 'Inventory':
// ....
break;
}
}
const dataArray = [
{
name: "Dashboard",
icon: Assets.Dashboard,
dropDown: false,
onClick: handleClick.bind(null, 'Dashboard'),
},
{
name: "Inventory",
icon: Assets.Inventory,
dropDown: true,
onClick: handleClick.bind(null, 'Inventory'),
},
{
name: "Reports",
icon: Assets.Reports,
dropDown: true,
onClick: handleClick.bind(null, 'Reports'),
},
];
return (
<>
{dataArray.map((data) =>
<RightTab key={data.name}
data={data}
classNames={classNames} />)}
</>
);
};
const RightTab = ({ data, classNames }) => {
return (
<div className={classNames.concat(['RightTab flex__container']).join(' ')}
onClick={data.onClick}>
<img src={data.icon} alt="Dashboard Icon" />
<p className="p__poppins">{data.name}</p>
{data.dropDown === true ? (
<div className="dropdown__icon">
<img src={Assets.Arrow} alt="Arrow" />
</div>
) : (
<div className="nothing"></div>
)}
</div>
);
};
I got a list of objects which im displaying on the screen with the help of .map function.
It looks like this:
Component 1:
let itemList = [
{
type: "White T-shirt",
id: 1,
cost: 300,
image: whiteTshirt
},
{
type: "Purple T-shirt",
id: 2,
cost: 350,
image: purpleTshirt
},
{
type: "Baseballcap",
id: 3,
cost: 150,
image: whiteCap
},
{
type: "Vice Golfball",
id: 4,
cost: 40,
image: golfball
},
{
type: "Mousepad",
id: 5,
cost: 200,
image: mousepad
}
];
let products = itemList.map(items => {
let item =
<div key={items.id}>
<h2>{items.type}</h2>
<img className="image" src={items.image}></img>
<p className="price">${items.cost}</p>
<button onClick={onBuy} className="buy-btn">Buy</button>
</div>
return item;
})
return(
{shoppingcart ? <Component2 /> : null}
<main> {products} </main>
)
Component 2:
const Comopnent2 = props => {
const [webshop, setWebshop] = useState(false);
return(
<div>
{webshop ? <Webshop /> : null }
<a href="/Webshop" onClick={e => { e.preventDefault(); setWebshop(true)}} >
<p className="to-shop"> Back to shop</p></a>
<h2 className="shopping-header">Your Shopping Cart</h2>
<div className="cart-container">
// Here i want to object that i clicked display
</div>
)
}
What i want is to push one specific object to another array in another component that i have. I want to do that when i click the button which calls the onBuy funcion. How do i manage that? Thanks.
First create a hook for the cart item inside component 1:
const [cartItem, setCartItem] = useState();
set cartItem whenever the button is clicked and onBuy is called:
onBuy (id) {
let checkoutItem = this.itemList.find(item => item.id === id)
setCartItem(checkoutItem)
}
You'll be required to pass item id when you declare the button which call onBuy function.
let products = itemList.map(items => {
let item =
<div key={items.id}>
<h2>{items.type}</h2>
<img className="image" src={items.image}></img>
<p className="price">${items.cost}</p>
<button onClick={onBuy(item.id)} className="buy-btn">Buy</button>
</div>
return item;
})
To pass this selection to component 2. You can pass it as a prop in component 1:
return(
{shoppingcart ? <Component2 item={cartItem} /> : null}
<main> {products} </main>
In the component 2 You can display the data accordingly from props:
const Comopnent2 = props => {
const [webshop, setWebshop] = useState(false);
return(
<div>
{webshop ? <Webshop /> : null }
<a href="/Webshop" onClick={e => { e.preventDefault(); setWebshop(true)}} >
<p className="to-shop"> Back to shop</p></a>
<h2 className="shopping-header">Your Shopping Cart</h2>
<div className="cart-container">
{prop.item.name} //whatever properties your cart item has, I have used name just for example
</div>
)
}
I'm creating a simple recipe app that uses edamam's API. The data returned from edamam doesn't have a unique ID for each recipe element. As a result, I'm trying to use UUID to create unique Ids for all recipes that are returned. However, neither of the solutions that I'm applying creates a unique id.
My Recipe Component
import React, {useState, useEffect} from 'react';
import Recipe_Tiles from './Recipe_Tiles'
import { API_ID, API_KEY} from '../../Config'
import uuid from 'uuid/v4'
import '../../App.css'
function Recipe() {
//Initialized states
const [Search, setSearch] = useState("");
const [Term, setTerm] = useState();
const [Recipe, setRecipe] = useState([]);
let id = uuid();
//useEffect will run again if the Term state is updated
useEffect(() => {
getAPI();
}, [Term]);
//method to retrieve recipes
const getAPI = () => {
fetch(`https://api.edamam.com/search?q=${Term}&app_id=${API_ID}&app_key=${API_KEY}`)
.then(response => response.json())
.then(response => {
console.log(response)
setRecipe(response.hits)
})
}
const handleChange = (e) => {
setSearch(e.target.value);
}
const handleSubmit = (e) => {
e.preventDefault();
setTerm(Search);
setSearch("");
}
return (
<div
className="App"
style = {{marginLeft: "15px"}}
>
<h1 style = {{textAlign: 'center'}}>The Recipe App</h1>
<form onSubmit = {handleSubmit}>
<div className="row inputs">
<div className="input-field col s6">
<label htmlFor="first_name"> Search for Recipes</label>
<input
className = "recipe-input"
type = "text"
value = {Search}
onChange = {handleChange}
/>
</div>
<button
className="btn waves-effect waves-light button-input"
type="submit"
onSubmit = {handleSubmit}
name="action">Search
<i className="material-icons right">local_dining</i>
</button>
</div>
</form>
{/*
The recipe tiles component is used to create the recipe cards.
Once data is retrieved from the API, it'll be passed as props to the component.
*/}
{Recipe.map(i => (
<Recipe_Tiles
title = {i.recipe.label}
calories = {i.recipe.calories}
image = {i.recipe.image}
URL = {i.recipe.url}
ingredients = {i.recipe.ingredients}
id = {id}
/>
))}
</div>
);
}
export default Recipe;
My Recipe_Tiles Component:
import React from 'react'
export default function Recipe_Tiles ({title, calories, image, URL,ingredients, id}) {
return(
<div class="card">
<div class="card-image waves-effect waves-block waves-light">
<img class="activator" src={image} alt = "this is a recipe"/>
</div>
<div class="card-content">
<span class="card-title activator grey-text text-darken-4"
style = {{fontSize: '15px',lineHeight:'20px', fontWeight: '700'}}>{title}
<i class="material-icons right">more_vert</i>
</span>
<br />
<hr style = {{width: '40%', marginLeft: '90px', marginBottom: '10px'}}/>
<p> Calories: {Math.floor(calories)} | Ingredients: {ingredients.length}</p>
<br />
<p><a href={URL}>View The Complete Recipe Here</a></p>
</div>
<div class="card-reveal">
<span
class="card-title grey-text text-darken-4"
style = {{fontSize: '15px', lineHeight:'20px', fontWeight: '700', marginRight: '20px',marginTop: '50px'}}>
Ingredients List:<i class="material-icons right">close</i></span>
<ol>
{ingredients.map(i => (
<li style = {{marginLeft: '-40px'}}>{i.text}</li>
))}
</ol>
<p>My id is {id}</p>
</div>
</div>
)
}
Solution attempt 1:
In my Recipe component,create a variable called id and set it to UUID(). Next, pass this variable as a props in my map function. The problem with this solution is that it creates a UUID that is the same for all recipes.
Solution attempt 2
In my Recipe component and in my map function, create a prop called Id and pass it this: i.uuid(). However, I receive a Unhandled Rejection (TypeError): i.uuid is not a function error.
Here is the data returned from Edamam's API:
q: "chicken",
from: 0,
to: 10,
more: true,
count: 168106,
hits: [
{
recipe: {
uri: "http://www.edamam.com/ontologies/edamam.owl#recipe_b79327d05b8e5b838ad6cfd9576b30b6",
label: "Chicken Vesuvio",
image: "https://www.edamam.com/web-img/e42/e42f9119813e890af34c259785ae1cfb.jpg",
source: "Serious Eats",
url: "http://www.seriouseats.com/recipes/2011/12/chicken-vesuvio-recipe.html",
shareAs: "http://www.edamam.com/recipe/chicken-vesuvio-b79327d05b8e5b838ad6cfd9576b30b6/chicken",
yield: 4,
dietLabels: [],
healthLabels: [],
cautions: [],
ingredientLines: [],
ingredients: [],
calories: 4055.7632762010808,
totalWeight: 2765.5901364771207,
totalTime: 60,
totalNutrients: {},
totalDaily: {},
digest: []
},
bookmarked: false,
bought: false
},
{
recipe: {
uri: "http://www.edamam.com/ontologies/edamam.owl#recipe_8275bb28647abcedef0baaf2dcf34f8b",
label: "Chicken Paprikash",
image: "https://www.edamam.com/web-img/e12/e12b8c5581226d7639168f41d126f2ff.jpg",
source: "No Recipes",
url: "http://norecipes.com/recipe/chicken-paprikash/",
shareAs: "http://www.edamam.com/recipe/chicken-paprikash-8275bb28647abcedef0baaf2dcf34f8b/chicken",
yield: 4,
dietLabels: [],
healthLabels: [],
cautions: [],
ingredientLines: [],
ingredients: [],
calories: 3033.2012500008163,
totalWeight: 1824.6125000003276,
totalTime: 0,
totalNutrients: {},
totalDaily: {},
digest: []
},
bookmarked: false,
bought: false
},
Any suggestions or feedback would be much appreciated!
I was also faced to this issue concerning how to get the recipe ID. Finally, I found a solution, maybe this will help you.
First of all, each recipe has an unique 'uri' from Edamam API. Inside the uri, you will be able to find the recipe-ID (after the '#recipe_).
So, you could try to extract the ID from the uri string by using a small function like that :
extractIdFromUri(uri) {
return uri.split('#recipe_').pop()
}
It's worked for me.
Update the hits array using map() to return a new array with the uniqueId as an additional key. This can be achieved using ES6 spread syntax
const updatedHits = response.hits.map((recipe) => {
uniqueId: uuid(),
...recipe
});
setRecipe(updatedHits);
I am tring to open a image modal on clicking the image.I am getting a list of image from the restapi.But my page shows nothing when its rendered.I am using this link as reference: https://www.w3schools.com/howto/tryit.asp?filename=tryhow_css_modal_img
getAllProjectRequirementImageList = () => {
axios.get(this.state.apiUrl+'/api/v1/visitRequirement/getAllByProjectId', {
params: { projectId: this.state.projectId }
}).then((response) => {
console.log("get with list ImageData",response.data.data);
this.setState({ ioImageListing: response.data.data });
}).catch((error)=>{ console.log("error",error); this.setState({ ioImageListing: [] }); });
};
componentDidMount() {
console.log('componentDidMount colling ...');
this.getAllProjectRequirementImageList();
// responsive
var modal = document.getElementById('myModal');
// Get the image and insert it inside the modal - use its "alt" text as a caption
var img = document.getElementById('myImg');
var modalImg = document.getElementById('img01');
var captionText = document.getElementById('caption');
if (img) {
img.onclick = () => {
modal.style.display = 'block';
modalImg.src = this.src;
captionText.innerHTML = this.alt;
};
}
// Get the <span> element that closes the modal
var span = document.getElementsByClassName('close')[0];
span.onclick = () => {
modal.style.display = 'none';
};
}
render() {
return (
<div>
<div>
{this.state.ioImageListing.map((io, key) => {
io.visitRequirementList.map((skill, j) => (
<img
id="myImg"
src={
this.state.apiUrl +
'/api/v1/visitImageRequirementInfo/getImageByImagePathId?imagePath=' +
skill.imagePath
}
alt="Snow"
style={width:"100%";maxWidth:"300px"}
/>
));
})}
</div>
<div id="myModal" className="modal">
<div>
<span className="close">×</span>
<img className="modal-content" id="img01" />
<div id="caption"></div>
</div>
</div>
</div>
);
}
}
and this is the json response i am getting from backend:
{
"data": [
{
"id": "8c83ac41-13b2-4827-96bc-dd251c4cf929",
"visitLocation": "bedroom",
"visitRequirementList": [
{
"imagePath": "visitImageDirectory/332c3b83-82d3-45b6-9660-309ebc3f246d.png",
},
{
"imagePath": "visitImageDirectory/332c3b83-82d3-45b6-9660-309ebc3f246d.png",
}
]
},
{
"id": "05c36c21-adc6-4fa3-9609-b3dea67b9e69",
"visitLocation": "kitchen",
"visitRequirementList": [
{
"imagePath": "visitImageDirectory/7678f04c-22bd-4735-9f7d-8c34db31b714.png"
},
]
}
],
"message": "data Found",
"status": "success"
}
How can i show a list of images in a modal calling data from restapi.My restapi works fine.Any help regarding this would be appreciated.
There are many things you are doing the non-react way in this example. First of all, here's my solution for your problem:
import React, { Component } from 'react';
export default class extends Component {
state = {
showModal: false,
caption: '',
modalSrc: '',
// ...rest of your state
};
componentDidMount() {
this.getAllProjectRequirementImageList();
}
render() {
return (
<div>
<div>
{this.state.ioImageListing.map((io, key) => {
io.visitRequirementList.map((skill, j) => {
const src = `${this.state.apiUrl}/api/v1/visitImageRequirementInfo/getImageByImagePathId?imagePath=${skill.imagePath}`;
const alt = 'Snow'; // or whatever
return (
<img
id="myImg"
src={src}
onClick={() => {
this.setState({ showModal: true, caption: alt, modalSrc: src });
}}
alt={alt}
style={{ width: '100%', maxWidth: '300px' }}
/>
);
});
})}
</div>
<div
id="myModal"
className="modal"
style={{ display: this.state.showModal ? 'block' : 'none' }}
>
<div>
<span className="close" onClick={() => this.setState({ showModal: false })}>
×
</span>
<img className="modal-content" id="img01" src={this.state.modalSrc} />
<div id="caption">
{this.state.caption}
</div>
</div>
</div>
</div>
);
}
}
For me the main takedowns from this are:
In React, if you want to access the DOM, you should never do it with getElementById, querySelector or any other vanilla javascript DOM manipulation methods. Instead, you should use React Ref. Mind you, you shouldn't use refs at all unless it's the only option.
If you want to change style on click, especially stuff like hide and show modal, the easiest way to do so would be by using state and defining a style that depend on that state, just like I did in the modal.
React has its own events. Javascript's regular onclick changes to React's onClick. You can read more about it here.
Hope this code works for you. Let me know how it goes or if you need anything else.
You should read again about rules and conventions when developing applications with React.
First, avoid using DOM manipulation directly like the way you modifying Modal element. Please do it in a React way. Try something like this:
<div id="myModal" className="modal" style={this.state.isShowModal ? "block" : "none"}>
<div>
<span className="close">×</span>
<img className="modal-content" id="img01" src={this.state.displayImgSrc}/>
<div id="caption">{this.state.displayImgCaption}</div>
</div>
</div>
And then move your myImg click event handler outside of componenDidMount. Make it a separated function and use it to directly bind when render from image list.
io.visitRequirementList.map((skill, j) => (
<img
id="myImg"
src={
this.state.apiUrl +
'/api/v1/visitImageRequirementInfo/getImageByImagePathId?imagePath=' +
skill.imagePath
}
onClick={this.imageClick}
alt="Snow"
style={width:"100%";maxWidth:"300px"}
/>
));
Of course, inside imageClick you should implement logic by changing React state instead of direct manipulation.
Hope this can help
I'm trying to practice React by rebuilding an agency website. I'm working on a section which has staff images, and clicking one of those images opens the relevant staff bio in a modal. The images and the bios are in separate containing divs.
It feels like I should be able to write one event handler that finds and opens the relevant bio depending on which image is clicked (maybe using something like the data attribute?), but I can't figure out what I'd need to add.
Currently I just have a click handler which toggles a piece of 'active' state. That state is then added as a className to toggle whether the modal is showing. Problem of course being that it doesn't differentiate between bios, so they all show regardless which bio is clicked on.
In case it's useful, here is my 'staff bio' component:
const StaffBio = (props) => {
return (
<div className={`teamMemberOverlay ${props.active}`} onClick={props.onClick}>
<div className="teamMemberExpanded">
<h6>{props.name}</h6>
<div className="seperator"></div>
<p className="title">{props.title}</p>
</div>
</div>
);
}
Which is being used like this:
<StaffBio name="NAME HERE" title="TITLE HERE" active={this.state.active} onClick={this.showBio} />
So far I've got the images set up as follows:
<img src={PaulIllustration} className="staffPhoto" onClick={this.showBio} />
And lastly, my event handler:
showBio() {
let toggle = this.state.active === 'is-active' ? '' : 'is-active';
this.setState({active: toggle});
}
class AboutUsSlider extends Component {
constructor(props) {
super(props);
this.showBio = this.showBio.bind(this)
this.next = this.next.bind(this)
this.state = { active: null }
}
next() {
this.refs.slider.slickNext()
}
showBio(id) {
this.setState({active: id});
}
hideBio(){
this.setState({active: null});
}
render() {
var settings = {...}
const people = [{name: 'Paul', title: 'some title'}, {name: 'Ben', title: 'other'}, ...];
return (
<div>
<Slider ref="slider" {...settings}>
<div className="sliderPage">
<h2>Meet our team</h2>
<div className="seperator"></div>
<div className="teamPhotos">
{ // When setting the index, you should use something unique, I'll use the name here.
people.map((p, index) =>
<img key={p.name} src={`${p.name} + 'Illustration'`} className="staffPhoto" onClick={() => this.showBio(index)}) />
}
</div>
<Button BGColor="#009ECC" text="Our process" onClick={this.next} />
</div>
</Slider>
{ this.state.active && <StaffBio name={people[this.state.active]} title={people[this.state.active].title} onClick={this.hideBio}/>
</div>
)
}
EDITED
There are a couple of things you can do.
Each person probably has an id to identify it. So you could modify your showBio to look like this:
showBio(id) {
this.setState({ active: id })
}
This way, you get which person is currently active in your state.
You also need to change your img
<img src={PaulIllustration} className="staffPhoto" onClick={() => this.showBio(PaulId)} />
Where PaulId would be different for each person.
And your StaffBio:
<StaffBio name="NAME HERE" title="TITLE HERE" active={this.state.active == personId} onClick={this.showBio} />
const StaffBio = (props) => {
return (
<div className={`teamMemberOverlay ${props.active ? 'is-active' : ''}`} onClick={props.onClick}>
<div className="teamMemberExpanded">
<h6>{props.name}</h6>
<div className="seperator"></div>
<p className="title">{props.title}</p>
</div>
</div>
);
}