I'm applying star rating to my react app.
so I have lists of star components. Once I click on rating it's applying for the all components.
Like I do star rating to first food, then it gets apply for all foods also.
I want to handle each food rating independently.
Can someone tell me where I'm going wrong ?
befor rating
after rating for one food
here is my..
import React, { useCallback, useEffect } from "react";
import "./styles.css";
import { Rating } from 'react-simple-star-rating'
import Star from './Start';
export default function App() {
const [category, setCategory] = React.useState("Indian");
const [rating, setRating] = React.useState(3);
const handleChange = (value) => {
setRating(value);
}
const Food = {
Indian: [
{
name: "Misal Pav",
description:
"Misal pav is a popular Maharashtrian street food of usal (sprouts curry) topped with onions, tomatoes, farsan (fried savory mixture), lemon juice, coriander leaves and served with a side of soft pav (Indian dinner rolls). The sprouts curry is made from moth bean sprouts. This misal recipe is a tasty and filling vegan dish that can be served as breakfast, lunch or brunch.",
rating: 5,
},
{
name: "Vada Pav",
description:
"Vada pav is a savory dinner roll stuffed with fried mashed and spiced potato fritters. It is a popular vegan street food snack eaten in Mumbai and rest of Maharashtra. This dish is full of flavors and various textures!",
rating: 1,
},
{
name: "Masala Dosa",
description:
"Masala Dosa / dosey / dosai is a variation of the popular South Indian dosa which has its origins in Tuluva Udupi cuisine of Karnataka. It is made from rice, lentils, potato, fenugreek, ghee and curry leaves, and served with chutneys and sambar. It is popular in South India.",
rating: 1,
}
],
Chinese: [
{
name: "Noodles",
description:
"Chinese noodles vary widely according to the region of production, ingredients, shape or width, and manner of preparation. Noodles were invented in China, and are an essential ingredient and staple in Chinese cuisine. They are an important part of most regional cuisines within China, and other countries with sizable overseas Chinese populations.",
rating: 2,
},
{
name: "chilly chicken ",
description:
"Chilli chicken is a popular Indo-Chinese dish of chicken of Hakka Chinese heritage. In India, this may include a variety of dry chicken preparations.",
rating: 2,
},
{
name: "Manchurian",
description:
"Veg Manchurian is a tasty Indo Chinese dish of fried veggie balls in a spicy, sweet and tangy sauce. ",
rating: 2,
}
],
Italian: [
{
name: "Pizza",
description:
"Though a slab of flat bread served with oil and spices was around long before the unification Italy, there’s perhaps no dish that is as common or as representative of the country as the humble pizza.",
rating: 3,
},
{
name: "Pasta",
description:
"pasta, any of several starchy food preparations (pasta alimentaria) frequently associated with Italian cuisine and made from semolina, the granular product obtained from the endosperm of a type of wheat called durum, and containing a large proportion of gluten (elastic protein).",
rating: 3,
},
{
name: "Lasagna",
description:
"Lasagna is a wide, flat pasta noodle, usually baked in layers in the oven. Like most Italian dishes, its origins are hotly contested, but we can at least say that’s its stronghold is in the region of Emilia-Romagna, where it transformed from a poor man’s food to a rich meal filled with the ragù, or meat sauce.",
rating: 3,
}
]
};
const handleChangeButton = (cat) => {
setCategory(cat);
};
useEffect(() => {
setCategory(category);
}, [setCategory, category]);
return (
<div className="App">
<h1>
<img
className="logo"
src="data:image/png;base64,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"
alt="Good Food"
/>
Good Food
</h1>
<p>Checkout my Favourite food items here ...</p>
<div className="btn-container">
{Object.keys(Food).map((cat, i) => (
<button
key={i}
className={category === cat ? "active" : ""}
onClick={() => handleChangeButton(cat)}
>
{cat}{" "}
</button>
))}
</div>
<h2>List</h2>
<div className="list-container">
{Food[category].map((foodie, i) => (
<div className="list-item" key={i}>
<Star
count={5}
size={40}
value={rating}
activeColor={'red'}
inactiveColor={'#ddd'}
onChange={handleChange} />
<h5>{foodie.description}</h5>
</div>
))}
</div>
</div>
);
}
It's because you are getting Star component's value props from one source which is rating.
You need to have an individual value for each Star components.
Replace your Star component with:
<Star
count={5}
size={40}
value={foodie.rating}
activeColor={'red'}
inactiveColor={'#ddd'}
onChange={handleChange}
/>
Edit:
Edit:
You also need to put the Food object inside a useState so you can edit the ratings for each food categories.
ex:
const [food, setFood] = useState({
Indian: [
{
name: "Misal Pav",
description:
"Misal pav is a popular Maharashtrian street food of usal (sprouts curry) topped with onions, tomatoes, farsan (fried savory mixture), lemon juice, coriander leaves and served with a side of soft pav (Indian dinner rolls). The sprouts curry is made from moth bean sprouts. This misal recipe is a tasty and filling vegan dish that can be served as breakfast, lunch or brunch.",
rating: 5,
},
{
name: "Vada Pav",
description:
"Vada pav is a savory dinner roll stuffed with fried mashed and spiced potato fritters. It is a popular vegan street food snack eaten in Mumbai and rest of Maharashtra. This dish is full of flavors and various textures!",
rating: 1,
},
{
name: "Masala Dosa",
description:
"Masala Dosa / dosey / dosai is a variation of the popular South Indian dosa which has its origins in Tuluva Udupi cuisine of Karnataka. It is made from rice, lentils, potato, fenugreek, ghee and curry leaves, and served with chutneys and sambar. It is popular in South India.",
rating: 1,
}
],
...
and to set Rating you can do:
const setRating = (type: string, food: string, rating: number) => {
setFood((prevFood) => {
return {
...prevFood,
[type]: prevFood[type].map((foodData: any) => {
if (foodData.name === food) {
return {
...foodData,
rating
}
}
return foodData;
})
}
})
}
This is happening because you are updating you state rating, and using this state in all your lists.
<Star
count={5}
size={40}
value={rating} // <----- state that get's updated
activeColor={'red'}
inactiveColor={'#ddd'}
onChange={handleChange} />
Instead you need to update the FOOD object. One way would be you can code your onChange such that it finds the element for which you selected starts, and based on that it can update the ratings property and then you can use that rating propery in component.
<Star
count={5}
size={40}
value={foodie.rating} <-- since you will update the FOOD object you'll get desired result.
activeColor={'red'}
inactiveColor={'#ddd'}
onChange={handleChange} />
You can update rating independently via useState
Here you don't have any unique id so i find name.
key refers to food[key] ,
ob refers which rating has to updated just pass entire object, like food[key][position]
const handleUpdateRating = (key, ob) => {
setFood((prevData) => ({
...prevData ,
[key]:prevData.[key].map((obj) =>
obj.name === ob.name ? {...obj , rating:ob.rating + 1} : obj
)
}))
};
Related
Okay, so I am currently writing a React application using Redux and Typescript and am having trouble coming up with a solution to a problem since I am pretty new to redux. It is a restaurant online order page where I have 2 components (MenuItems.tsx and Cart.tsx) inside of an Order.tsx page as well as a Rootreducer.tsx which has all of my menu information, my root reducer and the store:
The side to the left of the vertical black line on the page is everything within the MenuItems.tsx component, and the right side is the Cart.tsx component. When I click Add To Cart, the food is pushed into the items[] array within my state and is displayed in the cart.
The problem is that when I click "Add to Cart" on any of the items in a section, it returns every food item within the section, or the entire array that the action.payload contains (currently it sends all 5 food item names and its prices in the payload into the cart component). While it is working code, I specifically want it so that when I click the button on a particular food item, I only want the cart to display that one item, not all 5 meals in the Starters section.
My initial Menu data that is being accessed:
const initialMenu = [
{
section: "Starters",
list: [
{
img: chickenwings,
name: "Chicken Pub Wings",
desc: "Served with blue cheese dip and celery sticks. Choose your style plain and crispy, mild sticky barbecue or hot spicy Louisiana sauce",
price: "$9 for 6 wing basket, $14 for 12 wing basket",
},
{
img: soupoftheday,
name: "Soup of the Day",
desc: "Served with Dublin Soda Bread",
price: "$6",
},
{
img: potatoskins,
name: "Galway Potato Skins",
desc: "Served with Irish bacon, cheddar and chives",
price: "$9",
},
{
img: friedpickles,
name: "Fried Pickles",
desc: "Thick-cut chips, beer battered, with house-made ranch",
price: "$8",
},
{
img: mozzarellasticks,
name: "Mozzarella Sticks",
desc: "Slightly breaded, golden fried and served with warm marinara sauce",
price: "$8",
},
],
},
{
section: "Soups & Salads",
list: [
{
img: caesarsalad,
name: "Caesar Salad",
desc: "Crisp romaine, parmesan cheese, brioche croutons, classic dressing",
price: "$14",
},
{
img: cobbsalad,
name: "Cobb Salad",
desc: "Romaine, tomato, egg, red onion, avocado, blue cheese, smoked bacon",
price: "$15",
},
{
img: housesalad,
name: "House Salad",
desc: "Mixed greens, tomato, cucumber, croutons, shredded cheese. Can add grilled or fried chicken for $3",
price: "$10",
},
{
img: greeksalad,
name: "Greek Chicken Salad",
desc: "Romaine, black olives, banana peppers, tomato, cucumber, feta, and greek feta dressing",
price: "$11",
},
{
img: potatosoup,
name: "Loaded Potato Soup",
desc: "Topped with cheese and crispy bacon and served with dipping bread",
price: "$7",
},
{
img: beefstew,
name: "Guinness Beef Stew",
desc: "Braised beef, root vegetables and potatoes simmered in a Guinness broth",
price: "$14",
},
],
},
...
Code for MenuItems.tsx:
import { useAppSelector, useAppDispatch } from "../app/hooks";
import "../styles/Order.scss";
import "../App.tsx";
const MenuItems = (props: any): JSX.Element => {
const dispatch = useAppDispatch();
// console.log(props);
const menu = useAppSelector((state) => state.menu);
return (
<div>
<div className="menu-nav">
<ul>
<li>
Starters
</li>
<li>
Soups & Salads
</li>
<li>
Traditional Classics
</li>
<li>
Burgers & Sandwiches
</li>
<li>
Sides
</li>
<li>
Drinks
</li>
<li>
Desserts
</li>
</ul>
</div>
<div className="item-flex">
{menu.map((item, index) => {
return (
<div key={index} className="">
<h1 id={item.section}>{item.section}</h1>
<div className="item-grid">
{item.list.map((c, i) => {
return (
<div key={i} className="item-section">
<img src={c.img} alt="food image" />
<p>{c.name}</p>
<p>{c.desc}</p>
<p>{c.price}</p>
<button
key={c.name}
onClick={() =>
dispatch({
type: "ADD_TO_CART",
payload: item,
})
}
>
Add To Cart
</button>
</div>
);
})}
</div>
</div>
);
})}
</div>
</div>
);
};
export default MenuItems;
Code for Cart.tsx:
import { NavLink } from "react-router-dom";
import { useAppDispatch, useAppSelector } from "../app/hooks";
import "../styles/Order.scss";
const Cart = (): JSX.Element => {
const cart = useAppSelector((state) => state.items);
const dispatch = useAppDispatch();
return (
<div className="">
<h1>Cart</h1>
{cart.map((item, index) => {
return (
<div key={index}>
{item.list.map((c, i) => {
return (
<div key={i}>
<p>{c.name}</p>
<p>{c.price}</p>
<button
key={c.name}
onClick={() =>
dispatch({ type: "REMOVE_FROM_CART", payload: item })
}
>
Remove From Cart
</button>
</div>
);
})}
</div>
);
})}
<NavLink to="/ordercomplete">Order Complete</NavLink>
</div>
);
};
export default Cart;
rootReducer.tsx:
type Action = {
type: string;
payload?: any;
};
interface CommonFood {
img: any;
name: string;
desc: string;
price: string;
}
interface FoodDetail {
section: string;
list: Array<CommonFood>;
}
const initialMenu = [
...
};
type State = {
menu: FoodDetail[];
items: FoodDetail[];
};
const initialState: State = {
menu: initialMenu,
items: [],
};
const rootReducer = (state = initialState, action: Action) => {
switch (action.type) {
case "ADD_TO_CART":
const newItems = state.items;
console.log(newItems);
return { ...state, items: [...newItems, action?.payload] };
case "REMOVE_FROM_CART":
return {
...state,
items: [...state.items].filter((item) => item !== action.payload),
};
default:
return state;
}
};
export default rootReducer;
So, based on the code that I have given, how can I change the "ADD_TO_CART" action type in my root reducer so that instead of it giving all 5 items within the array, I can target one item in it? Or do I need to change something else? I know that I would need to target its index somehow to grab that particular value, but I have been struggling with what to try. Any help is immensely appreciated!
Your dispatch references the items in your menu state. Each item is in fact an object describing a menu section, not an actual cart item.
I know that I would need to target its index somehow to grab that
particular value, but I have been struggling with what to try.
As you've described the problem, you don’t need the index, since you're just pushing the item to the cart. You just need to pass the current food item c like so:
dispatch({ type: "ADD_TO_CART", payload: c })
However, if you add the same item to the cart more than once, you probably want to increment the count of that item rather than push the duplicate item to the cart.
And for this, you will need an id of some kind in order to know what item to increment as well as a quantity to track the count of each item.
Solution
First, your state.items should presumably be of type CommonFood[] not FoodDetail[], so that your cart represents individual items.
Then, CommonFood, or whatever you decide is used as your cart item, needs to have both an id and a quantity prop:
{
name: "Fried Pickles",
id: 0,
quantity: 0,
img: friedpickles,
desc: "Thick-cut chips, beer battered, with house-made ranch",
price: "$8",
}
You'll also want to have access to the current cart state in MenuItems.tsx so you can pass it to your action.
So, in MenuItems.tsx you'd have:
dispatch({ type: "ADD_TO_CART", payload: {cartItems, itemToAdd} })
Here, I've just used cartItems to replace state.items and itemToAdd to replace c from your current code. This just makes the next bits more readable.
Then, in your action:
case "ADD_TO_CART":
const { cartItems, itemToAdd } = action.payload;
const newCartItems = (cartItems, itemToAdd) => {
const existingCartItem = cartItems.find(
(cartItem) => cartItem.id === itemToAdd.id
);
if (existingCartItem) {
return cartItems.map((cartItem) =>
cartItem.id === itemToAdd.id
? { ...cartItem, quantity: cartItem.quantity + 1 }
: cartItem
);
}
return [...cartItems, { ...itemToAdd, quantity: 1 }];
};
return { ...state, items: newCartItems };
In the action, we are checking if the item in the cart exists.
If it does, we increment the quantity for that item.
If not, we add the item, and we set the quantity to 1.
I currently am building a fitness website and am looking to use Mantine UI accordion component which is based off Typescript. I built my react project with javascript. Is there a way to create a .tsx file and call it into my app.js file?
Here's the error I am currently receiving. Am I missing the export on the Accordian code?
Module not found: Error: Can't resolve './components/Faq' in '/Users/rodriguezmedia/Desktop/blended/src'
import { Group, Avatar, Text, Accordion } from '#mantine/core';
import React from 'react';
const charactersList = [
{
image: 'https://img.icons8.com/clouds/256/000000/futurama-bender.png',
label: 'Bender Bending Rodríguez',
description: 'Fascinated with cooking, though has no sense of taste',
content: "Bender Bending Rodríguez, (born September 4, 2996), designated Bending Unit 22, and commonly known as Bender, is a bending unit created by a division of MomCorp in Tijuana, Mexico, and his serial number is 2716057. His mugshot id number is 01473. He is Fry's best friend.",
},
{
image: 'https://img.icons8.com/clouds/256/000000/futurama-mom.png',
label: 'Carol Miller',
description: 'One of the richest people on Earth',
content: "Carol Miller (born January 30, 2880), better known as Mom, is the evil chief executive officer and shareholder of 99.7% of Momcorp, one of the largest industrial conglomerates in the universe and the source of most of Earth's robots. She is also one of the main antagonists of the Futurama series.",
},
{
image: 'https://img.icons8.com/clouds/256/000000/homer-simpson.png',
label: 'Homer Simpson',
description: 'Overweight, lazy, and often ignorant',
content: 'Homer Jay Simpson (born May 12) is the main protagonist and one of the five main characters of The Simpsons series(or show). He is the spouse of Marge Simpson and father of Bart, Lisa and Maggie Simpson.',
},
{
image: 'https://img.icons8.com/clouds/256/000000/spongebob-squarepants.png',
label: 'Spongebob Squarepants',
description: 'Not just a sponge',
content: 'SpongeBob is a childish and joyful sea sponge who lives in a pineapple with his pet snail Gary in the underwater city of Bikini Bottom. He works as a fry cook at the Krusty Krab, a job which he is exceptionally skilled at and enjoys thoroughly. ',
},
]
interface AccordionLabelProps {
label: string;
image: string;
description: string;
}
function AccordionLabel({ label, image, description }: AccordionLabelProps) {
return (
<Group noWrap>
<Avatar src={image} radius="xl" size="lg" />
<div>
<Text>{label}</Text>
<Text size="sm" color="dimmed" weight={400}>
{description}
</Text>
</div>
</Group>
);
}
function Demo() {
const items = charactersList.map((item) => (
<Accordion.Item label={<AccordionLabel {...item} />} key={item.label}>
<Text size="sm">{item.content}</Text>
</Accordion.Item>
));
return (
<Accordion initialItem={-1} iconPosition="right">
{items}
</Accordion>
);
}
Is there a way to create a .tsx file and call it into my app.js file?
No. You need to delete all of the typescript types (interfaces ...etc) and rename the file to .js.
You need a typescript (compiler) to convert the .tsx file into .js. Otherwise you cannot use .tsx files, since browser can only parse js.
I am working on an app and I want to pass in a key from a dictionary to a child screen. I have the dictionary in the parent, the code below shows what I want to accomplish
import ImageDetail from "../components/ImageDetail";
var Salad = {
GreekSalad: ["Greek Salad", "Romaine lettuce, cucumber, onion, olives, tomatoes, bell peppers, feta cheese dressed with lemon, salt, and olive oil dressing",require('../../assets/greeksalad.jpg'), 10],
ArabicSalad: ["Arabic Salad", "Diced cucumbers, tomato, green onion, parsley dressed with lemon, salt, and olive oil dressing",require('../../assets/arabicsalad.jpg'), 8],
Tabula: ["Tabouli", "fine chopped parsley, cracked wheat, tomatoes, and cucumber dressed with lemon salt and olive oil dressing", 8],
KHSS: ["KH Special Salad", "Romaine lettuce, cucumbers, onions, tomatoes, onions, apples, mint, bell peppers, sunflower seeds, and zaatar dressed with lemon, salt, and olive oil dressing", 12],
LSCup: ["Lentil Soup", "Flavorful lentil, carrot, and onion pureed soup(Cup 8oz)", 5],
LSBowl: ["Lentil Soup", "Flavorful lentil, carrot, and onion pureed soup(Bowl 12oz)", 7]
};
The next portion of the code is the part that is difficult for me to finish, specifically the TouchableOpacity passing the Salads. GreekSalad key and value into the next page
const Salads = () => {
return (
<View>
<ScrollView>
<View style={styles.backGround}>
<View style={styles.container}>
<Image style={styles.logostyle} source={require('../../assets/KabobHouseLogo.jpg')}/>
</View>
<Text style={styles.title}>Salads{"\n"}</Text>
<View style={styles.container1}>
<TouchableOpacity onPress={(Salad.GreekSalad) => {props.navigation.navigate("InnerMenu")}}>
<ImageDetail title={Salad.GreekSalad[0]} description={Salad.GreekSalad[1]} imageSource={Salad.GreekSalad[2]} price={Salad.GreekSalad[3]}/>
</TouchableOpacity>
<ImageDetail title={Salad.ArabicSalad[0]} description={Salad.ArabicSalad[1]} imageSource={Salad.ArabicSalad[2]} price={Salad.ArabicSalad[3]}/>
<ImageDetail title={Salad.Tabula[0]} description={Salad.Tabula[1]} imageSource={require('../../assets/KabobHouseLogo.jpg')} price={Salad.Tabula[2]}/>
<ImageDetail title={Salad.KHSS[0]} description={Salad.KHSS[1]} imageSource={require('../../assets/KabobHouseLogo.jpg')} price={Salad.KHSS[2]}/>
This is how you pass values in React Navigation to the next screen
// navigation.navigate('RouteName', { /* params go here */ })
const nextScreen = ()=>{
props.navigation.navigate("InnerMenu",{saladKey:Salad.GreekSalad[1]})
}
Then pass the function into onPress
<TouchableOpacity onPress={nextScreen}>
<ImageDetail title={Salad.GreekSalad[0]} description={Salad.GreekSalad[1]} imageSource={Salad.GreekSalad[2]} price={Salad.GreekSalad[3]}/>
</TouchableOpacity>
This is how you access them in the next screen
const childScreen = (props)=>{
const saladKey = props.route.params.saladKey;
}
Lastly I recommended that you don't make a habit of passing around values between screens. Instead for most cases you can use a thing called useContext which lets you share state between all your screens. Read more here (https://dmitripavlutin.com/react-context-and-usecontext/)
I'm following the tutorial on react.js and I got into this error I don't know what is the main cause behind this error
Thank you to everyone who contributes error:
TypeError: Cannot read property 'map' of undefined
import { Link } from "react-router-dom";
function About(props) {
const leaders = props.leaders.map((leader) => {
return <p>Leader {leader.name}</p>;
});
I'm importing leaders data in the main component
Main component:
import React, { Component } from "react";
import Home from "./HomeComponent";
import Contact from "./ContactComponent";
import About from "./AboutComponent";
import Menu from "./MenuComponents";
import Dishdetail from "./DishdetailComponent";
import Header from "./HeaderComponent";
import Footer from "./FooterComponent";
import { Dishes } from "../shared/dishes";
import { Comments } from "../shared/comments";
import { Leaders } from "../shared/leaders";
import { Promotions } from "../shared/promotions";
import { Switch, Route, Redirect } from "react-router-dom";
class Main extends Component {
constructor(props, context) {
super(props, context);
this.state = {
dishes: Dishes,
comments: Comments,
promotions: Promotions,
leaders: Leaders,
};
}
render() {
const HomePage = () => {
return (
<Home
dish={this.state.dishes.filter((dish) => dish.featured)[0]}
promo={this.state.promotions.filter((promo) => promo.featured)[0]}
leader={this.state.leaders.filter((leader) => leader.featured)[0]}
/>
);
};
const DishWithId = ({ match }) => {
return (
<Dishdetail
dish={this.state.dishes.filter((dish) => dish.id === parseInt(match.params.dishId, 10))[0]}
comments={this.state.comments.filter((comment) => comment.dishId === parseInt(match.params.dishId, 10))[0]}
/>
);
};
return (
<div>
<Header />
<Switch>
<Route path="/home" component={HomePage} />
<Route exact path="/menu" component={() => <Menu dishes={this.state.dishes} />} />
<Route path="/menu/:dishId" component={DishWithId} />
<Route exact path="/contactus" component={Contact} />
<Route exact path="/aboutus" component={About} />
<Redirect to="/home" />
</Switch>
<Footer />
</div>
);
}
}
export default Main;
The leader's file:
export const Leaders = [
{
id: 0,
name: "Peter Pan",
image: "/assets/images/alberto.png",
designation: "Chief Epicurious Officer",
abbr: "CEO",
featured: false,
description:
"Our CEO, Peter, credits his hardworking East Asian immigrant parents who undertook the arduous journey to the shores of America intending to give their children the best future. His mother's wizardry in the kitchen whipping up the tastiest dishes with whatever is available inexpensively at the supermarket, was his first inspiration to create the fusion cuisines for which The Frying Pan became well known. He brings his zeal for fusion cuisines to this restaurant, pioneering cross-cultural culinary connections.",
},
{
id: 1,
name: "Dhanasekaran Witherspoon",
image: "/assets/images/alberto.png",
designation: "Chief Food Officer",
abbr: "CFO",
featured: false,
description:
"Our CFO, Danny, as he is affectionately referred to by his colleagues, comes from a long-established family tradition in farming and produce. His experiences growing up on a farm in the Australian outback gave him a great appreciation for varieties of food sources. As he puts it in his own words, Everything that runs wins, and everything that stays, pay!",
},
{
id: 2,
name: "Agumbe Tang",
image: "/assets/images/alberto.png",
designation: "Chief Taste Officer",
abbr: "CTO",
featured: false,
description:
"Blessed with the most discerning gustatory sense, Agumbe, our CFO, personally ensures that every dish that we serve meets his exacting tastes. Our chefs dread the tongue lashing that ensues if their dish does not meet his exacting standards. He lives by his motto, You click only if you survive my lick.",
},
{
id: 3,
name: "Alberto Somayya",
image: "/assets/images/alberto.png",
designation: "Executive Chef",
abbr: "EC",
featured: true,
description:
"Award-winning three-star Michelin chef with wide International experience having worked closely with whos-who in the culinary world, he specializes in creating mouthwatering Indo-Italian fusion experiences. He says, Put together the cuisines from the two craziest cultures, and you get a winning hit! Amma Mia!",
},
];
This community is great
You are trying to iterate trough props.leaders which is undefined, it has to be array. In your scenario it seems there is a missing attribute and that's causing this issue.
Need to pass leaders to props in that <Route /> component as:
<Route exact path="/aboutus" component={<About leaders={this.state.leaders} />} />
I am creating a quiz app.
I would like to have the users' answer displayed on the results page of the quiz underneath each question.
So far I have mapped over all of the questions from the original data source along with all the correctAnswers and these are being displayed.
Now within the same .map I would like to also display (under each question) the
userAnswer which holds the data for the answer the user selected.
I thought that if I was in a .map, on each iteration I could go into the answers array within each iteration of the source data and .find which of those answers then matched the answer the user selected, if that makes sense?
At the moment the expected behaviour only works for the last question. i.e i see the correct answer and user answer for the last question.
Please see code below:
const ResultCard = ({ score, getQuestions, qbank, userAnswer }) => {
return (
<div>
<div>You scored {score} out of 5! </div>
<div className="playBtnBox">
<button className="playBtn" type="button" onClick={getQuestions}>
Play again
</button>
</div>
<div>
{qbank.map((questionObject, index) => {
return (
<div>
<div className="questionBox"> {questionObject.question}</div>
<div className="resultCardCorrect">
Correct Answer: {questionObject.correct}
</div>
<div className="resultCardCorrect">
User Answer:
{questionObject.answers.find(answer => answer === userAnswer)}
</div>
</div>
);
})}
</div>
</div>
);
};
Source data that I am mapping over:
const qBank = [
{
question:
"Virgin Trains, Virgin Atlantic and Virgin Racing, are all companies owned by which famous entrepreneur? ",
answers: ["Richard Branson", "Alan Sugar", "Donald Trump", "Bill Gates"],
correct: "Richard Branson",
questionId: "099099"
},
{
question:
'Where is the train station "Llanfairpwllgwyngyllgogerychwyrndrobwllllantysiliogogogoch"?',
answers: ["Wales", "Moldova", "Czech Republic", "Denmark"],
correct: "Wales",
questionId: "183452"
},
{
question:
"Which company did Valve cooperate with in the creation of the Vive?",
answers: ["HTC", "Oculus", "Google", "Razer"],
correct: "HTC",
questionId: "267908"
},
{
question: "What's the name of Batman's parents?",
answers: [
"Thomas & Martha",
"Joey & Jackie",
"Jason & Sarah",
"TodWhat is the most common surd & Mira"
],
correct: "Thomas & Martha",
questionId: "333247"
},
{
question: "name Wales?",
answers: ["Jones", "Williams", "Davies", "Evans"],
correct: "Jones",
questionId: "496293"
},
{
question:
"What was the name of the WWF professional wrestling tag team made up of the wrestlers Ax and Smash?",
answers: [
"Demolition",
"The Dream Team",
"The Bushwhackers",
"The British Bulldogs"
],
correct: "Demolition",
questionId: "588909"
}
]
];
This is where the userAnswer comes from QuestionBox.js component:
const QuestionBox = ({
question,
options,
correct,
incrementScore,
incrementResponse,
userAnswer
}) => {
const [response, setResponse] = useState(""); // text to display whether user response is correct or not initially is nothing.
const [answers, setAnswerFunction] = useState(options); // the answers to each question from the api
const computeAnswer = (answer, index) => {
if (answer === correct) {
setResponse("correct");
incrementScore();
incrementResponse();
userAnswer(answer);
} else {
setResponse("sorry wrong!");
incrementResponse();
userAnswer(answer);
}
};
return (
<div className="questionBox">
<div className="question"> {question} </div>
{answers.map((answer, index) => {
return (
<button
key={index}
className="answerBtn"
type="button"
onClick={() => {
setAnswerFunction([answer]); // sends the answer clicked by the user to the state
computeAnswer(answer); // sends the answer the user clicks to the computeAnswer function and sets the response to correct or wrong.
}}
>
{answer}
</button>
);
})}
{response === "correct" ? (
<div className="correctResponse"> {response} </div>
) : (
<div className="wrongResponse"> {response} </div>
)}
</div>
);
};
export default QuestionBox;
And how it is added to the state in the top level componenet which houses the ResultsCard.js
userAnswer = answer => {
this.setState({ userAnswer: answer }, () => {
console.log(this.state.userAnswer);
});
};