I'm a newbie in ReactJS. I'm creating an app that takes from json file data and which is stored in the public folder.
class App extends React.Component {
componentDidMount() {
axios.get('http://localhost:3000/pizza.json').then(data => {
store.dispatch(setPizza(data.Pizza))
})
}
<Route path="/" render = { () => <Main items={ this.props.items }/>} exact></Route>
Also have routing, where on the main page i wanted to display components.
So, I have Item component:
function Item({ image, name, types, sizes, price }) {
const availableTypes = ['0', '1'];
const availableSizes = [26, 30, 40];
const [activeType, setActiveType] = useState(types[0])
const [activeSize, setActiveSize] = useState(sizes[0])
const onSelectType = index => {
setActiveType(index)
}
const onSelectSize = index => {
setActiveSize(index)
}
return (
<div className="item">
<img src={ image } alt="pizza1" className="item-preview"/>
<h3 className="item-name">{ name }</h3>
<div className="item-dimantion">
<ul className="list">
{
availableTypes.map((type, index) => (
<li
key={type}
className={classNames({
choice: true,
active: activeType === index ? 'active' : '',
disable: types.includes(index)
})}
onClick={() => onSelectType(index)}>
{ type }
</li>
))}
</ul>
<ul className="list">
{
availableSizes.map((size, index) => (
<li key={ size }
className={classNames({
choice: true,
active: activeSize === index ? 'active' : '',
disable: sizes.includes(index)
})}
onClick={() => onSelectSize(index)}>
{ size } см.
</li>
))
}
</ul>
</div>
<div className="more">
<h4 className="price">от { price }</h4>
<button className="add">Добавить</button>
</div>
</div>
)
}
Then, I call Item-component in Main:
import { Categories, Sorting, Item } from '../index'
function Main({ items }) {
...
<div className="main__content__items">
{
items.map(obj => {
return (
<Item key={obj}></Item>
)
})
}
</div>
And here I have issue(TypeError: Cannot read property 'map' of undefined), which can not mend for long time... Could you explain me how to fix it?! Thx!!!!
All the code you have posted looks correct. Maybe, you didn't assign value to the items variable.
Try
console.log(this.props.items)
to check whether items are assigned properly. If it's undefined use an empty array.
Related
I want to pass emailID as the second parameter to . Can you help me to understand how to pass additional parameter in Curly braces as a function parameter and how to access it in AccountMenuSidebar.
Sorry for asking this basic question.I am a newbie to Javascript and React.
class Invoices extends Component {
render() {
var emailID="guest#somedomain.com";
const accountLinks = [
{
text: 'Account Information',
url: '/account/user-information',
icon: 'icon-user',
},
{
text: 'Notifications',
url: '/account/notifications',
icon: 'icon-alarm-ringing',
},
];
return (
<section className="ps-my-account ps-page--account">
<div className="container">
<div className="row">
<div className="col-lg-4">
<div className="ps-page__left">
<AccountMenuSidebar data={accountLinks} /> // Want to pass email id as second argument here
</div>
</div>
</div>
</div>
</section>
);
}
}
export default Invoices;
const Accountbar = ({ data }) => (
<aside className="ps-widget--account-dashboard">
<p>{email}</p>
<div className="ps-widget__content">
<ul>
{data.map((link) => (
<li key={link.text} className={link.active ? 'active' : ''}>
<Link href={link.url}>
<a>
<i className={link.icon}></i>
{link.text}
</a>
</Link>
</li>
))}
</ul>
</div>
</aside>
);
export default Accountbar;
<AccountMenuSidebar data={accountLinks} email={emailID} />
and
const Accountbar = (data , emaildID) => (...
or
const Accountbar = (props) => (...
and then you can use props like this...
<ul>
{props.data.map((link) => (
<li key={link.text} className={link.active ? 'active' : ''}>
<Link href={link.url}>
<a>
<i className={link.icon}></i>
{link.text}
</a>
</Link>
</li>
))}
</ul>
When you pass the props from Invoices, you usually acces them like this in AccountMenuSidebar:
<AccountMenuSidebar data={accountLinks} />
const AccountMenuSidebar = (props) => {
return (
<p>{props.data}</p>
)
}
However, using destructuring, which lets you directly unpack variables from an object in JavaScript, you can access the props like this instead:
<AccountMenuSidebar data={accountLinks} />
const AccountMenuSidebar = ({ data }) => {
return (
<p>{data}</p>
)
}
So if you want to send another prop, you can access it the same way, i.e.
<AccountMenuSidebar data={accountLinks} email={email} />
const AccountMenuSidebar = (props) => {
return (
<>
<p>{props.data}</p>
<p>{props.email}</p>
</>
)
}
or using destructuring:
<AccountMenuSidebar data={accountLinks} email={email} />
const AccountMenuSidebar = ({ data, email }) => {
return (
<>
<p>{data}</p>
<p>{email}</p>
</>
)
}
I have a small restaurant app which lets users order from a menu. The menu has three types: food, drink, dessert. The component structure from top to bottom is Order -> Menu -> MenuItem. I want to separate the menu page based on type (example: all food items are under a title called FOOD and so on). Right now, the Menu component receives the menu array as a prop from Order and renders MenuItem for each item in the array. Each item has a property called type. I omitted certain parts of the code unrelated to this issue for brevity.
//Order
export default function Order() {
const [menu, setMenu] = useState<Array<{}>>([]);
const [total, setTotal] = useState(0);
useEffect(() => {
apiFetch("menu").then((json) => setMenu(json.menu));
}, []);
async function handleSubmit(e: any) {
e.preventDefault();
const selectedItems = getSelectedItems(TotalStore);
apiFetch("order", "post", { selectedItems })
.then((json) => {
alert("Order has been submitted");
setTotal(0);
TotalStore.reset();
localStorage.setItem("last_order_id", json.order.id);
function checkOrderStatus() {
apiFetch(
`order/${json.order.id || localStorage.getItem("last_order_id")}`
).then((placedOrder) => {
const { order } = placedOrder;
if (order[0].status === 2) {
alert("Your order is ready!");
} else {
setTimeout(checkOrderStatus, 5000);
}
});
}
checkOrderStatus();
})
.catch((error) => {
alert("Server error");
});
}
function orderPlaced(total: number) {
return total !== 0 ? true : false;
}
return (
<div>
{menu.length > 0 ? (
<>
<div className="menu">
<div className="menu-title">Food Menu</div>
<form id="menu-form" onSubmit={handleSubmit} autoComplete="off">
<Menu onChange={itemChanged} props={menu} />
<button type="submit" disabled={!orderPlaced(total)}>
Place Order
</button>
</form>
</div>
<div className="order-total">
<h2>
Total: $<span>{total.toFixed(2)}</span>
</h2>
</div>
</>
) : (
<>Loading Menu</>
)}
</div>
);
}
//Menu
export default function Menu({ onChange, props }: MenuProps) {
return (
<div>
{props.map((food: any, index: number) => {
return (
<MenuItem
key={index}
onChange={onChange}
type={food.type}
item={food}
/>
);
})}
</div>
);
}
//MenuItem
export default function MenuItem({ onChange, item, type }: MenuItemProps) {
return (
<div>
<article className="menu-item" data-item-type={type}>
<h3 className="item-name">{item.name}</h3>
<input
type="number"
className="menu-item-count"
min="0"
value={data.count}
onChange={menuItemCountChange}
/>
<strong className="item-price">${item.price.toFixed(2)}</strong>
</article>
</div>
);
}
Here is what the page currently looks like:
You want to group your menu data by the food.type property. One way would be to sort the menu items into their food type "category, then rendering each group separately.
export default function Menu({ onChange, items }) {
const foodCategories = items.reduce((categories, item) => {
if (!categories[item.type]) {
categories[item.type] = []; // <-- new array for category type
}
categories[item.type].push(item); // <-- push item into category type
return categories;
}, {});
return (
<div>
{Object.entries(foodCategories).map(([type, foodItems]) => (
<div key={type}>
<h1>{type}</h1> // <-- category header
{foodItems.map((food, index) => ( // <-- map food items
<MenuItem
key={index}
onChange={onChange}
type={food.type}
item={food}
/>
))}
<div>
))}
</div>
);
}
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>
)
}
I have been attempting to toggle a class on click so that when I click on one of the mapped items in my Tasks component, I add the 'complete' class and put a line through that item (crossing items off of a todo list). However with my current code set up, when I click on one element to add the class, all the other elements get crossed out as well and vice versa.
Here is my current setup. The class 'complete' is what will add a line through one of the mapped items in the Tasks component.
import { Container, Row} from 'react-bootstrap';
import {Link} from 'react-router-dom';
import axios from 'axios';
const List = (props) =>{
return(
<div>
<Link style={{textDecoration:'none'}} to={`/lists/${props.listId}`} > <p className="list-item">{props.item}</p></Link>
</div>
)
}
const Tasks = (props) =>{
return(
<div onClick={props.onClick} className={props.className} >
<div className='task-item' >
<p >{props.item}</p>
</div>
</div>
)
}
export default class Display extends Component {
constructor(props){
super(props)
this.onCompletedTask = this.onCompletedTask.bind(this);
this.state = {
list: [],
tasks:[],
complete:false
}
}
componentWillUpdate(nextProps){
axios.get(`http://localhost:8080/lists/${this.props.match.params.listId}`)
.then(response =>{
this.setState({
tasks:response.data
})
})
}
componentDidMount(){
axios.get('http://localhost:8080/lists')
.then(response=>{
this.setState({
list:response.data
})
})
.catch(error =>{
console.log(error)
});
}
onCompletedTask(item){
this.setState({ complete: !this.state.complete});
}
listCollection(){
return(
this.state.list.map(item=>{
return(<List item = {item.title} listId={item._id} key = {item._id} />)
})
)
}
taskCollection(){
return(
this.state.tasks.map((item, index) =>{
return(<Tasks onClick = {()=>this.onCompletedTask(item)} className={this.state.complete ? 'complete': ''} item={item.task} key={index}/>)
})
)
}
render() {
return (
<div id='main' >
<Container>
<Row>
<div className="sidebar">
<h1 style={{fontSize:"25pt"}}>Lists</h1>
<div className="list-menu">
{this.listCollection()}
</div>
<form action='/new-list' method='GET'>
<div style={{textAlign:'center'}}>
<button className='list-button' style={{fontSize:'12pt', borderRadius:'5px'}}>
+ New List
</button>
</div>
</form>
</div>
<div className='tasks'>
<h1 style={{fontSize:'25pt'}}>Tasks</h1>
{this.taskCollection()}
<form action={`/lists/${this.props.match.params.listId}/new-task`} method='GET'>
<button className='task-button'>
+
</button>
</form>
</div>
</Row>
</Container>
</div>
)
}
}
Your state holds only a single completed value, which OFC toggle all tasks. You could instead store a map of completed tasks.
this.state = {
list: [],
tasks: [],
complete: {}, // <--- use empty object as simple map object
}
Update onCompletedTask to store some uniquely identifying property of a task, like an id field
onCompletedTask(item){
this.setState(prevState => ({
completed: {
...prevState.completed, // <--- spread existing completed state
[item.id]: !prevState.completed[item.id] // <--- toggle value
},
}));
}
Update. taskCollection to check the completed map by id
taskCollection = () => {
const { completed, tasks } = this.state;
return tasks.map((item, index) => (
<Tasks
onClick={() => this.onCompletedTask(item)}
className={completed[item.id] ? "complete" : ""} // <--- check completed[item.id]
item={item.task}
key={index}
/>
))
};
I have 4 different divs each containing their own button. When clicking on a button the div calls a function and currently sets the state to show a modal. Problem I am running into is passing in the index of the button clicked.
In the code below I need to be able to say "image0" or "image1" depending on the index of the button I am clicking
JS:
handleSort(value) {
console.log(value);
this.setState(prevState => ({ childVisible: !prevState.childVisible }));
}
const Features = Array(4).fill("").map((a, p) => {
return (
<button key={ p } onClick={ () => this.handleSort(p) }></button>
)
});
{ posts.map(({ node: post }) => (
this.state.childVisible ? <Modal key={ post.id } data={ post.frontmatter.main.image1.image } /> : null
))
}
I would suggest:
saving the button index into state and then
using a dynamic key (e.g. object['dynamic' + 'key']) to pick the correct key out of post.frontmatter.main.image1.image
-
class TheButtons extends React.Component {
handleSort(value) {
this.setState({selectedIndex: value, /* add your other state here too! */});
}
render() {
return (
<div className="root">
<div className="buttons">
Array(4).fill("").map((_, i) => <button key={i} onClick={() => handleSort(i)} />)
</div>
<div>
posts.map(({ node: post }) => (this.state.childVisible
? <Modal
key={ post.id }
data={ post.frontmatter.main.[`image${this.state.selectedIndex}`].image }
/>
: null
))
</div>
</div>
);
}
}
This is a good answer which explains "Dynamically access object property using variable": https://stackoverflow.com/a/4244912/5776910