I am building a solar system app that automatically generates stars. My stars are a React component that accept a couple of props. When the user clicks the star, I need the system state to update and also I want the navlink to point to the same value.
let randomTypeVariable = starList[Math.floor(Math.random() * 6 + 1)];
return (
<NavLink
to={`/${randomTypeVariable}`}
onClick={() => props.setSystem(`${randomTypeVariable}`)}
className="starWrapper"
>
However at the moment the to= prop and the onClick function are giving different results. I know that this is because the randomTypeVariable is running each time and giving different results.
These components are being randomly generated so I cannot have the variable be a constant value.
How can I assign both of these properties to be the same randomly generated variable?
For context here is the full star component
let randomTypeVariable = starList[Math.floor(Math.random() * 6 + 1)];
const makeStars = (Count = 5) => {
if (Count > 0) {
return (
<NavLink
to={`/${randomTypeVariable}`}
onClick={() => props.setSystem(`${randomTypeVariable}`)}
className="starWrapper"
>
<Star
starName={`${makeStarName()}`}
starPosition={positionList[Math.floor(Math.random() * 9 + 1)]}
starType={`${starList[Math.floor(Math.random() * 6 + 2)]}`}
></Star>
{makeStars(Count - 1)}
</NavLink>
);
}
};
It shouldn't give a different result as randomTypeVariable is not a function, I tried this also.
Maybe I am missing something but still useMemo can solve your problem
const makeStars = (Count = 5) => {
let randomTypeVariable = useMemo(() => starList[Math.floor(Math.random() * 6 + 1)], []);
if (Count > 0) {
return (
<NavLink
to={`/${randomTypeVariable}`}
onClick={() => props.setSystem(`${randomTypeVariable}`)}
className="starWrapper"
>
<Star
starName={`${makeStarName()}`}
starPosition={positionList[Math.floor(Math.random() * 9 + 1)]}
starType={`${starList[Math.floor(Math.random() * 6 + 2)]}`}
></Star>
{makeStars(Count - 1)}
</NavLink>
);
}
};
So I wasn't able to solve this as much as get around it.
The first thing I did was remove the onClick from my component
let randomTypeVariable = starList[Math.floor(Math.random() * 6 + 1)];
if (Count > 0) {
return (
<NavLink
to={`/${randomTypeVariable}`}
className="starWrapper"
>
<Star
starName={`${makeStarName()}`}
starPosition={positionList[Math.floor(Math.random() * 9 + 1)]}
starType={`${randomTypeVariable}`}
></Star>
{makeStars(Count - 1)}
</NavLink>
And considering I was still able to use the navigation path of to={/${randomTypeVariable}} I was able to gather the information I needed from the URL of the page instead of the state.
let url = window.location.href
let modifiedUrl = url.split('/')
props.setSystem(modifiedUrl[3])
At last, the randomly generated components are consistent. However this is more of an avoiding the problem than actually solving it. If anyone has an actual solution please let your voice be heard here.
useEffect(() => {
setShowProducts(true);
if (_cloneArray(currentProducts) > 0) {
sortByPrice();
}
// eslint-disable-next-line react-hooks/exhaustive-deps
}, []);
const defaultProducts = () => {
let sortedProducts = _cloneArray(currentProducts);
return sortedProducts;
}
const sortByPrice = (e) => {
let sortValue = e.value;
let sortedProducts = _cloneArray(currentProducts);
if (sortValue === "lowest") {
sortedProducts = _sortArray(sortedProducts, "locationPrice");
} else if (sortValue === "highest") {
sortedProducts = _sortArray(sortedProducts, "locationPrice", "desc");
} else if (sortValue === "default") {
sortedProducts = defaultProducts();
}
setCurrentProducts(sortedProducts);
}
return (
<Menu menuButton={<MenuButton><CgSortAz size={20}/></MenuButton>} onClick={sortByPrice}>
<MenuItem value="default">Default</MenuItem>
<MenuItem value="lowest">Price: Low to High</MenuItem>
<MenuItem value="highest">Price: High to Low</MenuItem>
</Menu>
)
So here I've created the sort items feature in ascending and descending order and I want to return to default state, which is not working after so many trials. Please I need some help here
The issue is that once you sort an array and you override your variable there is no way to tell what the original order was.
const numbers = [9,1,8,2,7,3,6,4,5];
numbers.sort((a, b) => a - b);
console.log(numbers); // [1,2,3,4,5,6,7,8,9]
There is no way to turn back [1,2,3,4,5,6,7,8,9] into the initial value, because there is no info stored about the initial value.
To solve this we should keep the original array around.
const numbers = [9,1,8,2,7,3,6,4,5];
// `sort()` mutates the array, so we have to make a copy first
// to prevent `numbers` from changing
let sorted = Array.from(numbers).sort((a, b) => a - b);
console.log(sorted); // [1,2,3,4,5,6,7,8,9]
Now if we want to restore the original order we can simply do:
sorted = numbers;
// or create a copy `Array.from(numbers)` if you intent to mutate `sorted`
The same applies for React. A common way to solve this would be to have 2 states. One containing the initial/default array, the second containing the sorted variant.
const [currentProducts, setCurrentProducts] = useState(...);
const [sortedProducts, setSortedProducts] = useState(currentProducts);
When sorting, store the result as sortedProducts. If you want to reset sortedProducts simply assign it to currentProducts.
const sortByPrice = (e) => {
let sortValue = e.value;
let products = _cloneArray(sortedProducts);
if (sortValue === "lowest") {
products = _sortArray(products, "locationPrice");
} else if (sortValue === "highest") {
products = _sortArray(products, "locationPrice", "desc");
} else if (sortValue === "default") {
products = currentProducts;
}
setSortedProducts(products);
}
Note that you should use sortedProducts in your view instead of currentProducts.
Since we never update currentProducts there is no real reason for it to be a state. This could just be a constant or a property (wherever the values comes from). If the values comes from an external API (or something async) it makes sense to keep currentProducts as an state, because it has to be set once fetched.
Here is an example that keeps the original order NUMBERS around and stores the sorted variant in a separate state sorted:
const NUMBERS = [9,1,8,2,7,3,6,4,5];
function Numbers() {
const [sorted, setSorted] = React.useState(NUMBERS);
const sort = (e) => {
switch (e.target.value) {
case "asc":
setSorted(Array.from(NUMBERS).sort((a, b) => a - b));
break;
case "desc":
setSorted(Array.from(NUMBERS).sort((a, b) => b - a));
break;
case "default":
setSorted(NUMBERS);
break;
}
};
return <React.Fragment>
<div className="sort-actions">
<button onClick={sort} value="asc">asc</button>
<button onClick={sort} value="desc">desc</button>
<button onClick={sort} value="default">default</button>
</div>
<p>{JSON.stringify(sorted)}</p>
</React.Fragment>;
}
ReactDOM.render(<Numbers />, document.querySelector("#numbers"));
<script src="https://unpkg.com/react#17/umd/react.production.min.js"></script>
<script src="https://unpkg.com/react-dom#17/umd/react-dom.production.min.js"></script>
<div id="numbers"></div>
I am new to reactJS. I am implementing a Quiz page, where users can answer the questions and once they submit answers, the result is displayed. Following is the DisplayResult component which calculates the score and displays the result. I am getting error at the if statement. Checked the syntax multiple times, not sure if it is a syntax issue or if I am missing something. Could you please help.
import React from "react";
function DisplayResult(props) {
var score=0;
var Answers=[1947,1950];
props.data.map((dat, i) =>
({if (dat===Answers[i]) {score++}}
));
return<div>Your answers are {props.data[0]}, {props.data[1]} and your score is {score} </div>;
}
export default DisplayResult;
Below is the error I am getting:
./src/DisplayResult.jsx
Line 8:14: Parsing error: Unexpected token, expected ","
6 | var Answers=[1947,1950];
7 | props.data.map((dat, i) =>
8 | ({if (dat===Answers[i]) {score++}}
| ^
9 | ));
10 | returnYour answers are {props.data[0]}, {props.data[1]} and your score is {score} ;
Use a forEach loop instead of .map.
By the way, you need to focus on the basics and fundamentals of React.
Try going through basic tutorials on Youtube.
const Answers = [1947, 1950];
function DisplayResult(props) {
let score = 0;
props.data.forEach((data, i) => {
if (data === Answers[i]) {
score += 1;
}
});
return (
<div>
Your answers are {props.data[0]}, {props.data[1]} and your score is{" "}
{score}{" "}
</div>
);
}
You have an extra parentheses on your map, it should be like this:
props.data.map((dat, i) => {
if (dat === Answers[i]) {
score++
}
})
You should use a forEach instead of a map since you're suppose to have a return value in a map which is not the case here. See documentation here: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/map
You shoould not be using parenthesis and using the curly braces inside of it, it seems that you are used to conditional rendering notation:
props.data.map((dat, i) =>
(
{if (dat===Answers[i]) {score++}}
)
);
write this instead:
props.data.map((dat, i) =>
{
if (dat===Answers[i]) {score++}
}
);
Use foreach instead of map since you're not returning anything. And you're wrapping all you if statement inside ( ) which makes means map method is trying to return the whole thing and will throw an error.
props.data.forEach((dat, i) => {
if (dat === Answers[i]) {
score++
}
})
So guys, I have this component:
const Iphone = ({phones,searchQuery}) => {
const filterIphone = phones.map((p, index) =>
(<div className="model" key={index}>
<NavLink to={'/p/' + p.id}>{p.body.model}</NavLink>
</div>
))
return (
<div>
{filterIphone}
</div>
);
};
export default Iphone;
phones - array with objects using which I return model(title of phones) from an object.
searchQuery- value which i get from input. (from Redux)
So, I want to make Search Bar, but I don't know how in this situatuon i can filter " filterIphone " beause i have used map before. I need to make function which filters my titles (model).
Try this,
const phones = searchQuery && searchQuery.trim().length ? phones.filter(p => {
if(p && p.body && p.body.model && p.body.model.toLowerCase().includes(searchQuery.toLowerCase())){
return p
}
}) : phones
#const filterIphone = ......
I am making a card game in React JS that requires 3 sets of unique cards.
The way the format works is there are ingredient cards that can create potions. The ingredients are dealt in the Top Row of the game, so I have the component called TopRow.
Since these are not normal playing cards I had to generate arrays with 10 of each of 5 different cards for the deal. ( shuffle(a) )
Then I am splicing the deal to only get 5 cards ( a.splice(5); )
So I want the value of the ingredients to increment based on the number of times the ingredients appear, example: function handleHoneyIncrement should increase countHoney by 1.
I've tried a couple different things and I guess I am having a brain fart on how to make a for loop for this.
function TopRow(props) {
let a=["Honey0", "Bone0", "Herbs0", "Mushroom0", "Seeds0",
"Honey1", "Bone1", "Herbs1", "Mushroom1", "Seeds1",
"Honey2", "Bone2", "Herbs2", "Mushroom2", "Seeds2",
"Honey3", "Bone3", "Herbs3", "Mushroom3", "Seeds3",
"Honey4", "Bone4", "Herbs4", "Mushroom4", "Seeds4",
"Honey5", "Bone5", "Herbs5", "Mushroom5", "Seeds5",
"Honey6", "Bone6", "Herbs6", "Mushroom6", "Seeds6",
"Honey7", "Bone7", "Herbs7", "Mushroom7", "Seeds7",
"Honey8", "Bone8", "Herbs8", "Mushroom8", "Seeds8",
"Honey9", "Bone9", "Herbs9", "Mushroom9", "Seeds9"
];
shuffle(a);
function shuffle(a) {
for (let i = a.length - 1; i > 0; i--) {
const j = Math.floor(Math.random() * (i + 1));
[a[i], a[j]] = [a[j], a[i]];
}
return a.splice(5);
}
let imageIngredients = a.map(image => {
return <img key={image} src={require(`../pngs/${image}.png`)}
alt="ingredients" className="img-responsive"
style={{width:"15%", float:"left"}}
/>
});
let handleHoneyIncrement = () => {
if (shuffle.length= "Honey0" ||"Honey1" ||"Honey2" ||"Honey3" ||"Honey4" ||"Honey5" ||"Honey6" ||"Honey7" || "Honey8" || "Honey9" ){
this.setState({countHoney: this.state.countHoney + 1})
};
};
return (
<div className="row" id="topRow"
style={{WebkitBorderRadius:2, WebkitTextStrokeColor: "red", width:"90%", maxHeight:"30%", padding:0}} >
<div className="col-6-md">
<img src={require('../pngs/IngredientBacks.png')} alt="ingredientsBack" style={{width:"15%", float:"left"}} />
</div>
<div className="col-6-md">
{imageIngredients}
{handleHoneyIncrement}
{a}
</div>
</div>
);}
export default TopRow;
Not 100% sure if this is what you were going for, but it sounds like you just need to turn the ingredients list into a collection of ingredient/count pairs?
const ingredientCounts = a.reduce((obj, curr) => ({
...obj,
[curr]: obj[curr] ? obj[curr] + 1 : 1
}), {})
ingredientCounts["Honey0"] // 1
If you're looking to count all Honeys together like Honey0 + Honey1, etc., this should work:
const ingredientCounts = a.reduce((obj, curr) => {
const keys = ["Honey", "etc"]; // maybe this list should be somewhere else, IDK
const key = keys.find(k => curr.includes(k)); // missing null check :)
return {
...obj,
[key]: obj[key] ? obj[key] + 1 : 1
}
}, {})
ingredientCounts["Honey"] // 10
Then we can set state for all of them like:
this.setState({
counts: ingredientCounts
})
And have a state of counts like:
{
Honey: 10,
etc: 0
}
I'm not 100% sure that I understand your goals correctly, but I think a simplified version is that you want to show:
5 random cards from your deck
A button or trigger that shuffles the deck and displays a new hand of 5 cards from the same deck
A count of the total number of honey cards accumulated as the hand is updated
There are a number of confusing things in your code sample, so rather than try to make corrections I threw up a quick demo of how I would approach that problem with some comments explaining what I did differently, given these assumptions. https://codesandbox.io/s/trusting-mclean-kwwq4
import React, { useState, useEffect } from "react";
// The deck of cards is probably a constant whose values never change directly.
// It's possible that I'm wrong and the deck *does* change, but even so I imagine
// it would come from a prop or context from a parent component. Either way the
// cards array should not be mutable.
const CARDS = [
"Honey0", "Bone0", "Herbs0", "Mushroom0", "Seeds0",
"Honey1", "Bone1", "Herbs1", "Mushroom1", "Seeds1",
"Honey2", "Bone2", "Herbs2", "Mushroom2", "Seeds2",
"Honey3", "Bone3", "Herbs3", "Mushroom3", "Seeds3",
"Honey4", "Bone4", "Herbs4", "Mushroom4", "Seeds4",
"Honey5", "Bone5", "Herbs5", "Mushroom5", "Seeds5",
"Honey6", "Bone6", "Herbs6", "Mushroom6", "Seeds6",
"Honey7", "Bone7", "Herbs7", "Mushroom7", "Seeds7",
"Honey8", "Bone8", "Herbs8", "Mushroom8", "Seeds8",
"Honey9", "Bone9", "Herbs9", "Mushroom9", "Seeds9"
];
const initialCards = [];
function TopRow(props) {
// Keep the current hand of cards in state rather than mutating an array
// directly in the function body. React function components should be pure,
// with all side effects occurring inside of effect hooks.
let [cards, setCards] = useState(initialCards);
let [honeyCount, setHoneyCount] = useState(
countSubstrings(initialCards, "Honey")
);
let imageIngredients = cards.map(image => (
<img
key={image}
src={require(`../pngs/${image}.png`)}
alt={humanReadableAltTag}
className="img-responsive"
style={{ width: "15%", float: "left" }}
/>
));
function shuffleCards() {
// Reset your hand of cards with the original array (the deck)
setCards(shuffleArray(CARDS));
}
// Return all state to initial values
function reset() {
setCards(initialCards);
setHoneyCount(countSubstrings(initialCards, "Honey"));
}
// Any time our cards are updated, we want to increment the number of Honey
// cards in our hand. useState accepts a lazy initializer to access the
// previous state, which is very useful for effects like this!
useEffect(() => {
setHoneyCount(count => count + countSubstrings(cards, "Honey"));
}, [cards]);
return (
<div
{...props}
className="row"
id="topRow"
style={
{
WebkitBorderRadius: 2,
WebkitTextStrokeColor: "red",
width: "90%",
maxHeight: "30%",
padding: 0
}
}
>
<button onClick={shuffleCards}>
{cards.length ? "Shuffle" : "Deal"}
</button>
<button onClick={reset}>Reset</button>
<hr />
<div className="col-6-md">
<img
src={require("../pngs/IngredientBacks.png")}
alt="Back of ingredient card"
style={{ width: "15%", float: "left" }}
/>
</div>
<div className="col-6-md">
{imageIngredients}
</div>
<hr />
<div>
<strong>TOTAL HONEY COUNT:</strong> {honeyCount}
</div>
</div>
);
}
export default TopRow;
// I put these utility functions outside of the component body since there is no
// real reason to recreate them on each render.
/**
* #param {any[]} array
*/
function shuffleArray(array) {
for (let i = array.length - 1; i > 0; i--) {
const j = Math.floor(Math.random() * (i + 1));
[array[i], array[j]] = [array[j], array[i]];
}
// Use slice instead of splice here to prevent mutating the original array
return array.slice(0, 5);
}
/**
* #param {string[]} array
* #param {string} subs
*/
function countSubstrings(array, subs) {
return array.filter(card => card.includes(subs)).length;
}