I have got this UI below. When I first open this component ModalColorList and click goNext(), it does not go next at first but the second time. After that, it goes next well, so it means the user needs to click twice the button in order to go next.
In the case of goPrev() it works fine, but it does not seem to be clean either.
I usually google before beginning to code, but this time, I would like to try by myself, so maybe this does not work as expected. Please let me know the better way to make goPrev and goNext smoother.
ModalColorList
const ModalColorList = ({ data }) => {
const [isLoading, setIsLoading] = useState(false);
const [large, setLarge] = useState({
idx: data?.index,
name: data?.name,
src: data?.image,
});
const onColorClick = (color, idx) => {
setLarge({
idx,
name: color.node.name,
src: color.node.imageAttribute.image.mediaItemUrl,
});
};
const goPrev = () => {
let count = 0;
if (large.idx === 0) {
count = data.data.length - 1;
} else {
count = large.idx - 1;
}
setLarge({
idx: count,
name: data?.data[count]?.node.name,
src: data?.data[count]?.node.imageAttribute.image.mediaItemUrl,
});
};
const goNext = () => {
let count = data.index++;
if (data.index > data.data.length) {
data.index = 0;
count = 0;
}
setLarge({
idx: count,
name: data?.data[count]?.node.name,
src: data?.data[count]?.node.imageAttribute.image.mediaItemUrl,
});
};
useEffect(() => {
setIsLoading(true);
}, []);
return (
<>
{isLoading && (
<div >
<div>
<div onClick={goPrev}>
<RiArrowLeftSLine />
</div>
<div>
<Image src={large.src} objectFit="cover" />
</div>
<div className="icon-wrap -right-[50px]" onClick={goNext}>
<RiArrowRightSLine />
</div>
</div>
<ul>
{data.data.map((color, idx) => (
<li key={color.node.id} onClick={() => onColorClick(color, idx)} >
<div className={` ${large.idx === idx && 'border-[#f7941d] border-4'}`}>
<Image src={color.node.imageAttribute.image.mediaItemUrl} />
</div>
</li>
))}
</ul>
</div>
)}
</>
);
};
export default ModalColorList;
Kindly let me
I'm not sure why didn't things didn't work for you, as there was very little code that I could analysis. But till what you have shared, the component should have been working properly.
I have cleaned your work some what and create a codesandbox for the same, hope it gives you some idea.
If this doesn't help, please do share a codesandbox instance where the behavior is reproduceable I will check on it.
CODESANDBOX LINK
ModalColorList.js
import { useState, useEffect } from "react";
const ModalColorList = ({ data }) => {
const [isLoading, setIsLoading] = useState(true);
const [large, setLarge] = useState({
idx: data?.index,
name: data?.name,
src: data?.image
});
const onColorClick = (color, idx) => {
setLarge({
idx,
name: color.name,
src: color.src
});
};
const goPrev = () => {
let count = 0;
const collection = data.collection;
if (large.idx === 0) {
count = collection.length - 1;
} else {
count = large.idx - 1;
}
setLarge({
idx: count,
name: collection[count].name,
src: collection[count].src
});
};
const goNext = () => {
let count = 0;
const collection = data.collection;
if (large.idx + 1 >= collection.length) {
count = 0;
} else {
count = large.idx + 1;
}
console.log(collection, count);
setLarge({
idx: count,
name: collection[count].name,
src: collection[count].src
});
};
return (
<>
{isLoading && (
<div>
<div>
<div onClick={goPrev}>Left</div>
<div onClick={goNext}>Right</div>
<div>
<img src={large.src} objectFit="cover" />
</div>
</div>
<ul>
{data.collection.map((color, idx) => (
<li key={idx} onClick={() => onColorClick(color, idx)}>
<div
className={` ${
large.idx === idx && "border-[#f7941d] border-4"
}`}
>
<img src={color.src} />
</div>
</li>
))}
</ul>
</div>
)}
</>
);
};
export default ModalColorList;
colorSample.js
export default {
name: "glassy",
src:
"https://images.unsplash.com/photo-1604079628040-94301bb21b91?ixlib=rb-1.2.1&ixid=MnwxMjA3fDB8MHxwaG90by1wYWdlfHx8fGVufDB8fHx8&auto=format&fit=crop&w=687&q=80",
index: 0,
collection: [
{
name: "glassy",
src:
"https://images.unsplash.com/photo-1604079628040-94301bb21b91?ixlib=rb-1.2.1&ixid=MnwxMjA3fDB8MHxwaG90by1wYWdlfHx8fGVufDB8fHx8&auto=format&fit=crop&w=687&q=80"
},
{
name: "pop",
src:
"https://images.unsplash.com/photo-1498940757830-82f7813bf178?ixlib=rb-1.2.1&ixid=MnwxMjA3fDB8MHxwaG90by1wYWdlfHx8fGVufDB8fHx8&auto=format&fit=crop&w=1074&q=80"
},
{
name: "milky",
src:
"https://images.unsplash.com/photo-1556139943-4bdca53adf1e?ixlib=rb-1.2.1&ixid=MnwxMjA3fDB8MHxwaG90by1wYWdlfHx8fGVufDB8fHx8&auto=format&fit=crop&w=687&q=80"
}
]
};
App.js
import "./styles.css";
import ModalColorList from "./ModalColorList";
import colorSample from "./colorSample";
export default function App() {
return (
<div className="App">
<h1>Hello CodeSandbox</h1>
<h2>Start editing to see some magic happen!</h2>
<ModalColorList data={colorSample} />
</div>
);
}
if you understand you have list and want to navigate with next and pre, simple you can create temp array, use push and pop to add and remove items then always show the last item like that.
yourArray[yourArray.length - 1]
let me show you example.
let currentIndex = 0;
const mylistItem = [1,2,3,4,5];
let showItems = [mylistItem[currentIndex]] // default show first item always showing last item
const lastItem = showItems[showItems.length - 1]; // the item want to display
for Moving forward
showItems.push(myListItem[currentIndex+1]);
currentIndex += 1;
for Moving back
showItems.pop();
currentIndex -= 1;
Related
I'm trying to make a ecommerce store and I want to render a <CompanyCard/> component after every 2 <ProductCard/> components.
I've never done anything like this so I'm not sure if I'm doing the right approach
Here is a example of what I want:
<ProductCard/>
<ProductCard/>
<CompanyCard/>
<ProductCard/>
<ProductCard/>
<CompanyCard/>
(and so on..)
but for some reason im getting a blank page with these 2 errors in the console
The above error occurred in the <CompanyCard> component:
Uncaught TypeError: Cannot read properties of undefined (reading '_id')
This is my code (I'm also using React Redux)
function HomePage() {
let counter = 0;
const dispatch = useDispatch();
const productList = useSelector((state) => state.productList);
const { error, loading, products } = productList;
const companyList = useSelector((state) => state.companyList);
const { companies } = companyList;
useEffect(() => {
dispatch(listProducts(), listCompanies());
}, [dispatch]);
return (
<>
<Navbar />
<div className="homePage">
<div className="centerItemsContainer">
<div className="productsContainer">
{products.map((product) => {
counter++;
if (counter % 4 === 0) {
const companyIndex = counter / 4 - 1;
const company = companies[companyIndex];
return (
<>
<CompanyCard company={company} />
<ProductCard product={product} />
</>
);
} else {
return <ProductCard product={product} />;
}
})}
</div>
</div>
</div>
<Footer />
</>
);
}
export default HomePage;
I hope this helps if u want to render company on 4 th place pls change it to 4 same goes for what place u want
const products = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11];
export const App = () => {
return (
<>
{products.map((product, index) => {
const counter = index + 1;
if (counter % 2 === 0) {
return (
<>
<p>Prod{index}</p>
<p>Company</p>
</>
);
}
return <p>Prod{index}</p>;
})}
</>
);
};
First, it seems like you are simply selecting a company that does not exist. Log companyIndex and see what values you are using.
Also, there is no need to manually keep track of counter, it is the second argument from map, so just write:
{products.map((product, counter) => {
You are getting an error, because your iterator exceeds array length
I would rather use a normal for loop for this:
const products1 = [...Array(10).keys()].map((e) => "p" + e);
const companies1 = [...Array(10).keys()].map((e) => "c" + e);
console.log(getList(products1, companies1));
const products2 = [...Array(10).keys()].map((e) => "p" + e);
const companies2 = [...Array(1).keys()].map((e) => "c" + e);
console.log(getList(products2, companies2));
function getList(products, companies) {
const list = [];
const min = Math.min(products.length, companies.length);
for (
let i = 0, p = 0, c = 0;
i < products.length + companies.length;
i += 1
) {
// every fifth is a company, but only if there is a company left
// or there are no more products
if ((c < companies.length && i % 5 === 4) || p >= products.length) {
list.push(companies[c++]);
} else {
list.push(products[p++]);
}
}
return list;
}
Final component:
function HomePage() {
let counter = 0;
const dispatch = useDispatch();
const productList = useSelector((state) => state.productList);
const { error, loading, products } = productList;
const companyList = useSelector((state) => state.companyList);
const { companies } = companyList;
function getList() {
const list = [];
const min = Math.min(products.length, companies.length);
for (
let i = 0, p = 0, c = 0;
i < products.length + companies.length;
i += 1
) {
if ((c < companies.length && i % 5 === 4) || p >= products.length) {
list.push(<CompanyCard company={companies[c++]} />);
} else {
list.push(<ProductCard product={products[p++]} />);
}
}
return list;
}
useEffect(() => {
dispatch(listProducts(), listCompanies());
}, [dispatch]);
return (
<>
<Navbar />
<div className="homePage">
<div className="centerItemsContainer">
<div className="productsContainer">{getList()}</div>
</div>
</div>
<Footer />
</>
);
}
export default HomePage;
{products.map((item, index) => (
<>
<ProductCard />
{(!!index && !((index + 1) % 5) && <CompanyCard />)}
</>
)}
I am trying to manipulate the images shown in my React App by voice. I implemented the SR, which works fine:
<button onClick={SpeechRecognition.startListening}>Start</button>
I have an array of loadImages in my state, which is empty first:
const [loadImages, setLoadImages] = React.useState([]);
Whenever the word spoken is "kitten", the array of loadImages gets updated in this way:
if(transcript == "kitten")
{
const newImages = loadImages.concat({image: 'https://www.onlinekittencare.com/wp-content/uploads/2020/07/vChK6pTy3vN3KbYZ7UU7k3-1200-80.jpg'})
setLoadImages(newImages);
}
The transcript you see is a variable, which I initialized here:
const {transcript} = useSpeechRecognition();
In the render I use it to show what the SR understood, so if I say "hello" it shows "hello":
<p id="transcript">Transcript: {transcript}</p>
And this is where the images in loadImages show up:
{images.map((image) => {
return <URLImage image={image}/>;
})}
The problem is that whenever I say "kitten", which as stated above is used as a command to add the picture to the array loadImages, my React App gets blank. In the inspect I can also see that it says react-dom.development.js:14997 Uncaught Error: Too many re-renders. React limits the number of renders to prevent an infinite loop.
How do I fix that?
EDIT (I added the whole code):
function App() {
const [rectangles, setRectangles] = useState([]);
const [circles, setCircles] = useState([]);
const [selectedId, selectShape] = useState(null);
const [shapes, setShapes] = useState([]);
const [, updateState] = React.useState();
const stageEl = React.createRef();
const layerEl = React.createRef();
const fileUploadEl = React.createRef();
const [brushSize, setBrushSize] = React.useState('5');
const [isDrawing, setIsDrawing] = React.useState(false);
const dragUrl = React.useRef();
//const stageRef = React.useRef();
const [images, setImages] = React.useState([]);
const [loadImages, setLoadImages] = React.useState([]);
const getRandomInt = max => {
return Math.floor(Math.random() * Math.floor(max));
};
const {transcript} = useSpeechRecognition();
const URLImage = ({image}) => {
const [img] = useImage(image.src);
return (
<Image
image = {img}
x = {image.x}
y = {image.y}
offsetX = {50}
offsetY = {50}
width={200}
height={200}
draggable
/>
);
};
const drawLine = () => {
setIsDrawing(true);
if(isDrawing){
addLine(stageEl.current.getStage(), layerEl.current, brushSize);
};
};
const eraseLine = () => {
addLine(stageEl.current.getStage(), layerEl.current, brushSize, "erase");
};
const addRectangle = () => {
setIsDrawing(false);
const rect = {
x: getRandomInt(100),
y: getRandomInt(100),
width: 100,
height: 100,
fill: "red",
id: `rect${rectangles.length + 1}`,
};
const rects = rectangles.concat([rect]);
setRectangles(rects);
const shs = shapes.concat([`rect${rectangles.length + 1}`]);
setShapes(shs);
};
const forceUpdate = React.useCallback(() => updateState({}), []);
const undo = () => {
const lastId = shapes[shapes.length - 1];
let index = circles.findIndex(c => c.id == lastId);
if (index != -1) {
circles.splice(index, 1);
setCircles(circles);
}
index = rectangles.findIndex(r => r.id == lastId);
if (index != -1) {
rectangles.splice(index, 1);
setRectangles(rectangles);
}
index = images.findIndex(r => r.id == lastId);
if (index != -1) {
images.splice(index, 1);
setImages(images);
}
shapes.pop();
setShapes(shapes);
forceUpdate();
};
document.addEventListener("keydown", ev => {
if (ev.code == "Delete") {
let index = circles.findIndex(c => c.id == selectedId);
if (index != -1) {
circles.splice(index, 1);
setCircles(circles);
}
index = rectangles.findIndex(r => r.id == selectedId);
if (index != -1) {
rectangles.splice(index, 1);
setRectangles(rectangles);
}
index = images.findIndex(r => r.id == selectedId);
if (index != -1) {
images.splice(index, 1);
setImages(images);
}
forceUpdate();
}
});
if(transcript == "kitten")
{
const newImages = loadImages.concat({image: 'https://www.onlinekittencare.com/wp-content/uploads/2020/07/vChK6pTy3vN3KbYZ7UU7k3-1200-80.jpg'})
setLoadImages(newImages);
}
return (
<div className="home-page">
{loadImages.map(image => (
<img id="img" className="img"
src={image.image}
width="200"
height="200"
onDragStart={(e) => {
dragUrl.current = e.target.src;}}
/>
))}
<div
onDrop={(e) => {
e.preventDefault();
// register event position
stageEl.current.setPointersPositions(e);
// add image
setImages(
images.concat([
{
...stageEl.current.getPointerPosition(),
src: dragUrl.current,
},
])
);
}}
onDragOver={(e) =>
e.preventDefault()
}
>
<h1>Whiteboard</h1>
<button onClick={addRectangle}>
Rectangle
</button>
<button>
Circle
</button>
<button onClick={drawLine}>
Line
</button>
<button onClick={eraseLine}>
Erase
</button>
<select
value={brushSize}
onChange={(e) => {
setBrushSize(e.target.value);
drawLine();
}}
>
<option value="5">5</option>
<option value="20">20</option>
<option value="50">50</option>
</select>
<button variant="secondary">
Text
</button>
<button variant="secondary">
Image
</button>
<button variant="secondary" onClick={undo}>
Undo
</button>
<p id="transcript">Transcript: {transcript}</p>
<button onClick={SpeechRecognition.startListening}>Start</button>
<Stage
width={window.innerWidth * 0.9}
height={window.innerHeight - 150}
ref={stageEl}
dragabble
onMouseDown={e => {
// deselect when clicked on empty area
const clickedOnEmpty = e.target === e.target.getStage();
if (clickedOnEmpty) {
selectShape(null);
}
}}
>
<Layer ref={layerEl}>
{rectangles.map((rect, i) => {
return (
<Rectangle
key={i}
shapeProps={rect}
isSelected={rect.id === selectedId}
//onSelect={() => {
// selectShape(rect.id);
//}}
onChange={newAttrs => {
const rects = rectangles.slice();
rects[i] = newAttrs;
setRectangles(rects);
}}
/>
);
})}
{images.map((image) => {
return <URLImage image={image}/>;
})}
</Layer>
</Stage>
</div>
</div>
);
}
export default App;
Based on the code you've shared, it has to do with how you're updating the state if the transcript is equal to kitten.
Essentially, the logic you've written says, on render, if the transcript is kitten, update the state. BUT, when you update the state, that will re-render, and hit that logic again... and again... and again. The solution here is to wrap that in a useEffect – React Docs explain it best but in simple terms, you want to "do something" as a side effect of "something else".
In this case, if the transcript updates, you want to check the state of transcript, and if it meets a condition, you want to update your state:
React.useEffect(() => {
if (transcript === "kitten") {
const newImages = loadImages.concat({image: 'https://www.onlinekittencare.com/wp-content/uploads/2020/07/vChK6pTy3vN3KbYZ7UU7k3-1200-80.jpg'})
setLoadImages(newImages);
}
}, [transcript]);
The final piece to the useEffect is a dependency array ([transcript])–this dictates to React which item you want to watch for changes in–if transcript changes, it will run your effect and only when it changes, instead of every time it renders.
I am trying to rewrite a small app from vanilla js to react, and in one element I encountered a problem with passing on values in the inputs. What this element does, is after selecting a number it generates that many inputs to fill, and after filling send its id and value further (value can also be empty)
In Vanilla Js I did it with id and querySelector, but in React I have a trouble to change it correct
React code:
import React, { useState, useEffect } from "react";
import "./style.css";
import Values from "./Values";
export default function App() {
const [numberValue, setNumberValue] = useState("");
const [inputValues, setInputValues] = useState([]);
const [sendValues, setSendValues] = useState(false);
const [inputs, setInputs] = useState([]);
let numbers = [4, 6, 8];
//reset teamsName on change teamsValue
useEffect(() => {
for (let i = 1; i <= numberValue; i++) {
setInputValues(prev => [
...prev,
{
id: i,
value: ""
}
]);
}
}, [numberValue]);
const showButtons = numbers.map((number, i) => (
<button
className={`${numberValue === number ? "button active" : "button"}`}
onClick={() => {
setNumberValue(number);
setInputValues([]);
setInputs([]);
showInputs();
}}
>
{number}
</button>
));
//let inputs = [];
const showInputs = () => {
for (let i = 1; i <= numberValue; i++) {
setInputs(prev => [
...prev,
<input
type="text"
className="input"
placeholder={`Input ${i}`}
//value={inputValues.find(input => input.id === i && input.value)}
onChange={e =>
inputValues.filter(
input =>
input.id === i &&
setInputValues([
...inputValues,
{ id: i, value: e.target.value }
])
)
}
/>
]);
}
};
return (
<>
<div className="button-group">{showButtons}</div>
{numberValue && (
<>
<h3 className="title">Your inputs</h3>
<div className="input-group">{inputs}</div>
</>
)}
<button onClick={() => setSendValues(true)}>SEND</button>
{sendValues && <Values inputValues={inputValues} />}
</>
);
}
JS:
const buttonGroup = document.querySelector(".button-group");
const inputGroup = document.querySelector(".input-group");
const inputValues = document.querySelector(".input-values");
let n;
const showInputs = number => {
n = number;
inputGroup.innerHTML = ''
for (let i = 1; i <= number; i++) {
inputGroup.innerHTML += `
<input type="text" name="name" id="input-${i}" class="input" placeholder="team name"> <br>
`;
}
};
let values = []
const showValues = () => {
//clear
inputValues.innerHTML = '';
values = [];
//show new
for (let i = 1; i <= n; i++) {
const input_val = document.querySelector(`#input-${i}`).value;
values.push({
id: i,
value: input_val
});
}
for(let i = 0; i<=n; i++){
inputValues.innerHTML += `
<p>id: ${values[i].id} value:${values[i].value}
</p>
`
}
};
Links to code:
React -> https://stackblitz.com/edit/react-uw9dzc?file=src/App.js
JS -> https://codepen.io/Arex/pen/qBqLVBq?editors=1111
I took the liberty to simplify your code a bit. Basically I assigned value as it's own variable const value = e.target.value; as it is a synthetic event and tends to get lost if you pass it further down, so this preserves the value. Also, I changed inputValues to an object to make it easier to update:
// App.js
export default function App() {
const [numberValue, setNumberValue] = useState("");
const [inputValues, setInputValues] = useState({});
const [sendValues, setSendValues] = useState(false);
let numbers = [4, 6, 8];
const showButtons = numbers.map((number, i) => (
<button
className={`${numberValue === number ? "button active" : "button"}`}
onClick={async () => {
await setNumberValue(number);
await setInputValues({});
}}
>
{number}
</button>
));
return (
<>
<div className="button-group">{showButtons}</div>
{numberValue && (
<>
<h3 className="title">Your inputs</h3>
<div className="input-group">
{[...new Array(numberValue)].map((_value, id) => (
<input
type="text"
className="input"
placeholder={`Input ${id}`}
onChange={e => {
const value = e.target.value;
setInputValues(prev => {
prev[id] = value;
return prev;
});
}}
/>
))}
</div>
</>
)}
<button onClick={() => setSendValues(true)}>SEND</button>
{sendValues && <Values inputValues={inputValues} />}
</>
);
}
// Values.js
const Values = ({ inputValues }) => {
const showValues = Object.keys(inputValues).map(input => (
<div>
{input} : {inputValues[input]}
</div>
));
return <div>{showValues}</div>;
};
export default Values;
There are multiple issues in the shared code as follows:
It is not recommended to store components in state, in the shared code you are storing <input/> component in state. For more details check this
Unnecessary states are being used, always try to keep a minimum number of states as more number of states as more states are needed to be managed, making things unnecesarily complicated.
using previous state syntax to generate new state where it is not needed.
I am adding a working code with minimum changes for you reference.
App.js
import React, { useState, useEffect } from "react";
import "./style.css";
import Values from "./Values";
export default function App() {
const [numberValue, setNumberValue] = useState('');
const [inputValues, setInputValues] = useState([]);
const [sendValues, setSendValues] = useState(false);
let numbers = [4, 6, 8];
//reset teamsName on change teamsValue
useEffect(() => {
setInputValues(
Array(numberValue).fill("")
);
setSendValues(false)
}, [numberValue]);
const showButtons = numbers.map((number, i) => (
<button
className={`${numberValue === number ? "button active" : "button"}`}
onClick={() => {
setNumberValue(number);
}}
>
{number}
</button>
));
return (
<>
<div className="button-group">{showButtons}</div>
{numberValue && (
<>
<h3 className="title">Your inputs</h3>
<div className="input-group">
{inputValues.map((val, i) => (
<input
key={`input${i}`}
type="text"
className="input"
placeholder={`Input ${i+1}`}
value={val}
onChange={(e) => {let newValues = inputValues.slice(); newValues[i]=e.target.value; setInputValues(newValues)}
}
/>
))}
</div>
</>
)}
<button onClick={() => setSendValues(true)}>SEND</button>
{sendValues && <Values inputValues={inputValues} />}
</>
);
}
Values.js
import React from "react";
const Values = ({ inputValues }) => {
const showValues = inputValues.map((input, i) => (
<div key={'output'+i}>
{i+1} : {input}
</div>
));
return <div>{showValues}</div>;
};
export default Values;
I am also sharing a updated stackblitz code reference you shared for better understanding Updated snippet with fixes for reference.
You have a GroceryApp component, which receives a list of products, each one with name and votes. The app should render an unordered list, with a list item for each product. Products can be upvoted or downvoted.
By appropriately using React state and props, implement the upvote/downvote logic. Keep the state in the topmost component, while the Product component should accept props.
const Product = props => {
const {name,votes} = props.product
const plus = () => {
// Call props.onVote to increase the vote count for this product
props.onVote(1,props.index);
};
const minus = () => {
// Call props.onVote to decrease the vote count for this product
props.onVote(-1,props.index);
};
return (
<li>
<span>{name}</span> - <span>votes: {votes}</span>
<button onClick={plus}>+</button>{" "}
<button onClick={minus}>-</button>
</li>
);
};
const GroceryApp = (props) => {
var [products, setProducts] = React.useState(props.products);
const onVote = (dir, index) => {};
return (
<ul>
{products.map((product,index) => (
<Product product={product} index={index} onVote={onVote} />
))}
{/* Render an array of products, which should call onVote when + or - is clicked */}
</ul>
);
}
document.body.innerHTML = "<div id='root'></div>";
ReactDOM.render(
<GroceryApp
products={[
{ name: "Oranges", votes: 0 },
{ name: "Bananas", votes: 0 }
]}
/>, document.getElementById('root')
);
let plusButton = document.querySelector("ul > li > button");
if (plusButton) {
plusButton.click();
}
console.log(document.getElementById('root').outerHTML)
You can update your products and sort them during rendering (can be costly if your list is very large).
const GroceryApp = (props) => {
const [products, setProducts] = React.useState(props.products);
const onVote = (dir, index) => {
const nextProducts = [...products];
const product = products[index];
nextProducts[index] = { ...product, votes: product.votes + dir };
setProducts(nextProducts)
};
return (
<ul>
{products
.sort((a, b) => b.votes - a.votes) // descending sort
.map((product,index) => (
<Product product={product} index={index} onVote={onVote} />
))
}
</ul>
);
}
This worked for me
import React from "react";
const Product = (props) => {
const plus = (ind) => {
props.onVote("+", ind);
// console.log({ ind });
};
const minus = (ind) => {
props.onVote("-", ind);
// console.log({ ind });
};
return (
<li>
<span>{props.product.name}</span> -{" "}
<span>votes: {props.product.votes}</span>
<button onClick={() => plus(props.index)}>+</button>{" "}
<button onClick={() => minus(props.index)}>-</button>
</li>
);
};
const GroceryApp = () => {
let [products, setProducts] = React.useState([
{ name: "Oranges", votes: 0 },
{ name: "Bananas", votes: 0 },
]);
const onVote = (dir, index) => {
const Products = [...products];
if (dir === "+") {
Products[index].votes = Products[index].votes + 1;
setProducts(Products);
} else {
Products[index].votes = Products[index].votes - 1;
setProducts(Products);
}
};
return (
<ul>
{products.map((product, index) => (
<Product key={index} onVote={onVote} index={index} product={product} />
))}
</ul>
);
};
export default GroceryApp;
today I offer you a new challenge
my problem and the next
in the following code
I map objects in my database
I also map a list of items
and so basically I would like to pass my database and not the Items table
basically I want to do exactly the same thing as in the following code except that instead of using the items array, I would like to be able to use the data array which contains my database
do you have any idea how to do this?
I hope I was precise thanks to you for the help Neff
ps: Sorry for the length of the code i try to do my best to clean a little
import React, { Component } from 'react';
import { CardText, Card,Row, Col, Button } from 'reactstrap';
import axios from 'axios'
import GridLayout from 'react-grid-layout';
import SmsForm from './Sms/SMSForm'
import FruitList from './FruitList'
import './AdminPage.scss'
const UP = -1;
const DOWN = 1;
const entrypoint = process.env.REACT_APP_API_ENTRYPOINT+'/api';
class AdminPage extends Component {
constructor(props) {
super(props);
this.state = {
items: [
{ id: 1, name: "orange", bgColor: "#f9cb9c" },
{ id: 2, name: "lemon", bgColor: "#fee599" },
{ id: 3, name: "strawberry", bgColor: "#e06666" }
],
data: [],
}
handleMove = (id, direction) => {
const { items } = this.state;
const position = items.findIndex(i => i.id === id);
if (position < 0) {
throw new Error("Given item not found.");
} else if (
(direction === UP && position === 0) ||
(direction === DOWN && position === items.length - 1)
) {
return; // canot move outside of array
}
const item = items[position]; // save item for later
const newItems = items.filter(i => i.id !== id); // remove item from array
newItems.splice(position + direction, 0, item);
this.setState({ items: newItems });
};
// rest of the component
onHandleChange(event) {
const name = event.target.getAttribute('name');
this.setState({
message: { ...this.state.message, [name]: event.target.value }
});
}
getRandom = async () => {
const res = await axios.get(
entrypoint + "/alluserpls"
)
this.setState({ data: res.data })
}
componentDidMount() {
this.getRandom()
}
render() {
let datas = this.state.data.map(datass => {
const status = JSON.parse(localStorage.getItem("validated-order") || "{}")[datass.id];
return (
<div>
< Col sm="12" key={datass.id} className="wtfFuHereIsForOnlyBackGroundColorForCol12Nice">
<FruitList fruitList={this.state.items} onMove={this.handleMove} />
<GridLayout className="GridlayoutTextOnlyForGridOuiAndHeigthbecauseHeigthWasBug" layout={layout} cols={12} rowHeight={30} width={1200}>
<div key="a">
<Card body className="yoloCardBodyForbackGroundAndRaduisBorderForAdminPageWtf">
<CardText className="cardTextForAdminPageForDataName"> Commande de {datass.name}</CardText>
</Card>
</div>
</ Col>
</div>
)
})
return (
<div> <div>
<Row className="wtfHereIsAgainOnlyForRowAndMarginForAdminPageJustWtf">
<div className="isJustForOnlyPaddingOnRowForAdminPage" >
<div className="navBarGridMenuAdminPage">
<div className="thatIsOnlyForSpaceArroundOnDivAdminPage">
<CardText className="maybeColForAdminPageOuiOui"> Nouvelles commandes </CardText>
</div>
</div>
<div>
{datas}
</div>
</div>
</Row>
</div>
<div className="box">
</div>
</div>
)
}
}
export default AdminPage
here my second components
import React from "react";
const LEFT = -1;
const RIGHT = 1;
class FruitList extends React.Component {
render() {
const { fruitList, onMove } = this.props;
return (
<div style={{ display: "flex" }}>
{fruitList.map(item => (
<div
key={item.id}
style={{
backgroundColor: item.bgColor,
display: "flex"
}}
>
<div className="fruitsArrows">
<a onClick={() => onMove(item.id, LEFT)}>←</a>
</div>
<div className="fruitsId">{item.id}</div>
<div className="fruitsName">{item.name}</div>
<div className="fruitsArrows">
<a onClick={() => onMove(item.id, RIGHT)}>→</a>
</div>
</div>
))}
</div>
);
}
}
export default FruitList;
To delete particular list do like this-
pass your item(object).
<a onClick={() => onMove(item)}>→</a>
handleMove function
handleMove = (row) => {
let filtered = this.state.items.filter(item=>item.id!==row.id);
this.setState({ items: filtered});
};