I am building todoList that when I check the box, the component is disappeared. I tried to identify each checkbox with index!
So, I make state:checkedItem, which have a "false or true" state in each checkbox. But, it doesn't render right away.
Here is my code sandbox : https://codesandbox.io/s/old-voice-6xyovb?file=/src/App.js
import "./styles.css";
import React from "react";
const todos = [
{ id: 0, value: "Wash the dishes" },
{ id: 1, value: "have lunch" },
{ id: 2, value: "listen to music" },
{ id: 3, value: "running" },
{ id: 4, value: "work out" }
];
export default function App() {
const [items, setItems] = React.useState(todos);
const [checkedItems, setCheckedItems] = React.useState(
new Array(todos.length).fill(false)
);
const checkHandler = ({ target }, idx) => {
checkedItems[idx] = !checkedItems[idx];
setCheckedItems(checkedItems);
//I want to identify idx ..
};
return (
<div className="App">
{items.map((todo, idx) => (
<div key={idx}>
<span>{todo.value}</span>
<input
type="checkbox"
checked={checkedItems[idx]}
onChange={(e) => checkHandler(e, idx)}
></input>
</div>
))}
</div>
);
}
The issue is that you call setCheckedItems but you give it the exact same array, so this is not considered a change.
To fix this, make sure you give it a new array:
setCheckedItems([...checkedItems]);
Here, I've updated your sandbox with notes and fixes.
https://codesandbox.io/s/objective-ioana-b04r8f?file=/src/App.js
Related
I am creating an e-commerce app where I am trying to apply filters e.g. filter by gender and/or brands. The filters only work individually and if I try to click both, the other filter cancels.
Please help! thank you in advance.
I am creating an e-commerce app where I am trying to apply filters e.g. filter by gender and/or brands. The filters only work individually and if I try to click both, the other filter cancels.
Please help! thank you in advance.
Sorry about the duplicate of question, Stackoverflow kept stopping me from posting because I have too much codes instead of description.
import React, { useState, useEffect } from 'react';
import './Clothing.css'
import data from '../../data/data2.json';
const Clothing = () => {
const [items, setItems] = useState([]);
const [color, setColor] = useState(null);
const types = [
{ id: 11, value: 'All' },
{ id: 22, value: 'Cap' },
{ id: 33, value: 'Sweatshirt' }
]
const genders = [
{ id: 55, value: 'Men' },
{ id: 66, value: 'Women' }
]
const brands = [
{ value: 'Graver' },
{ value: 'LMC' }
]
useEffect(() => {
setItems(data);
}, [])
const handleGender = (e) => {
const filteredItems = data.filter((d) => {
return d.gender === e.target.value
})
setItems(filteredItems)
}
const handleBrand = (e) => {
const filteredItems = data.filter((d) => {
return d.brand === e.target.value
})
setItems(filteredItems)
}
return (
<div className="bodyDiv">
<div className="leftMenu">
<div className='leftList'>
<h3>Gender</h3>
{
genders.map((g) =>
<button
id={g.id}
value={g.value}
onClick={(e) => {
handleGender(e);
handleColor(e);
}}
>{g.value}</button>
)
}
</div>
<div className='leftList'>
<h3>Brand</h3>
{
brands.map((b) =>
<button
id={b.id}
value={b.value}
onClick={(e) => {
handleBrand(e);
}}
>{b.value}</button>
)
}
</div>
<div className='itemSection'>
<div className='itemCount'>
Total {items.length}
</div>
<div className="itemLists">
{items.length === 0
? "No items found"
:
items.map((item) =>
<div className="itemCard" key={item.id}>
<img src={item.img} alt='clothes' />
<div className="itemText">
<h4>{item.brand}</h4>
<h4>{item.gender}</h4>
</div>
</div>
)}
</div>
</div>
</div>
)
}
export default Clothing
I have solved this problem. Message me if you need help.
i'm building a checkbox todo List that checked checkbox is disappearing.
I have two problems.
checked checkbox remains after re-rendering
When the two checkboxes are left, they dont disappear.
Here is my codeSandBox:-----
I think this might be a setState issue, setItems([...items.filter((item) => !checkedItems[item.id])]); -> rerendering (this scope havecheckedItems ={false,true,false,false,false}) so, checkbox is remaining?
import "./styles.css";
import React from "react";
const todos = [
{ id: 0, value: "Wash the dishes" },
{ id: 1, value: "have lunch" },
{ id: 2, value: "listen to music" },
{ id: 3, value: "running" },
{ id: 4, value: "work out" }
];
export default function App() {
const [items, setItems] = React.useState(todos);
const [checkedItems, setCheckedItems] = React.useState(
new Array(todos.length).fill(false)
);
const checkHandler = (idx) => {
checkedItems[idx] = !checkedItems[idx];
setItems([...items.filter((item) => !checkedItems[item.id])]);
setCheckedItems([...checkedItems]);
};
return (
<div className="App">
{items.map((todo, idx) => (
<div key={idx}>
<span>{todo.value}</span>
<input
type="checkbox"
checked={checkedItems[idx]}
onChange={() => checkHandler(idx)}
></input>
</div>
))}
</div>
);
}
It is not good practice to use index as key when iterating objects.
Since you have id, use this.
{items.map(todo => (
<div key={todo.id}>
<span>{todo.value}</span>
<input
type="checkbox"
checked={checkedItems[todo.id]}
onChange={() => checkHandler(todo.id)}
></input>
</div>
))}
You mess with indexes and the result is confusing.
If you want the items to be persisted and shown, just remove this line
setItems([...items.filter((item) => !checkedItems[item.id])]);
Demo
You do not need to have a separate checkedItems state. You can add a field checked in your todo object.
const todos = [
{ id: 0, value: "Wash the dishes", checked: false },
{ id: 1, value: "have lunch", checked: false },
{ id: 2, value: "listen to music", checked: false },
{ id: 3, value: "running", checked: false },
{ id: 4, value: "work out", checked: false }
];
export default function App() {
const [items, setItems] = React.useState(todos);
const checkHandler = (idx) => {
setItems(items.filter((item) => item.id !== idx));
};
return (
<div className="App">
{items.map((todo, idx) => (
<div key={idx}>
<span>{todo.value}</span>
<input
type="checkbox"
checked={todo.checked}
onChange={() => checkHandler(todo.id)}
></input>
</div>
))}
</div>
);
}
The key mistake you're making here is in onChange={() => checkHandler(idx)} and then using idx as your variable to filter out your items. idx does NOT equal the ids you have in your todo list, it will change based on the number of items left in the array.
The filter can also be improved to just be
setItems([...items.filter((item) => item.id !== idx)]);
The final code should look something like this (I'm not sure what checkedItems is meant to be doing or what it's for so it's not a consideration in this answer).
import "./styles.css";
import React from "react";
const todos = [
{ id: 0, value: "Wash the dishes" },
{ id: 1, value: "have lunch" },
{ id: 2, value: "listen to music" },
{ id: 3, value: "running" },
{ id: 4, value: "work out" }
];
export default function App() {
const [items, setItems] = React.useState(todos);
const [checkedItems, setCheckedItems] = React.useState(
new Array(todos.length).fill(false)
);
const checkHandler = (idx) => {
checkedItems[idx] = !checkedItems[idx];
setItems([...items.filter((item) => item.id !== idx)]);
setCheckedItems([...checkedItems]);
};
return (
<div className="App">
{items.map((todo, idx) => (
<div key={idx}>
<span>{todo.value}</span>
<input
type="checkbox"
onChange={() => checkHandler(todo.id)}
></input>
</div>
))}
</div>
);
}
Simply remove this line:
setItems([...items.filter((item) => !checkedItems[item.id])]);
that causes the list items to be filtered.
I am trying to create a simple button multi-select in React but I'm currently getting unexpected behaviour. I'd like users to be able to toggle multiple buttons and have them colourise accordingly, however the buttons seem to act a bit randomly.
I have the following class
export default function App() {
const [value, setValue] = useState([]);
const [listButtons, setListButtons] = useState([]);
const BUTTONS = [
{ id: 123, title: 'button1' },
{ id: 456, title: 'button2' },
{ id: 789, title: 'button3' },
];
const handleButton = (button) => {
if (value.includes(button)) {
setValue(value.filter((el) => el !== button));
} else {
let tmp = value;
tmp.push(button);
setValue(tmp);
}
console.log(value);
};
const buttonList = () => {
setListButtons(
BUTTONS.map((bt) => (
<button
key={bt.id}
onClick={() => handleButton(bt.id)}
className={value.includes(bt.id) ? 'buttonPressed' : 'button'}
>
{bt.title}
</button>
))
);
};
useEffect(() => {
buttonList();
}, [value]);
return (
<div>
<h1>Hello StackBlitz!</h1>
<div>{listButtons}</div>
</div>
);
}
If you select all 3 buttons then select 1 more button the css will change.
I am trying to use these as buttons as toggle switches.
I have an example running #
Stackblitz
Any help is much appreciated.
Thanks
I think that what you want to achieve is way simpler:
You just need to store the current ID of the selected button.
Never store an array of JSX elements inside a state. It is not how react works. Decouple, only store the info. React component is always a consequence of a pattern / data, never a source.
You only need to store the necessary information, aka the button id.
Information that doesn't belong to the state of the component should be moved outside. In this case, BUTTONS shouldn't be inside your <App>.
Working code:
import React, { useState } from 'react';
import './style.css';
const BUTTONS = [
{ id: 123, title: 'button1', selected: false },
{ id: 456, title: 'button2', selected: false },
{ id: 789, title: 'button3', selected: false },
];
export default function App() {
const [buttons, setButtons] = useState(BUTTONS);
const handleButton = (buttonId) => {
const newButtons = buttons.map((btn) => {
if (btn.id !== buttonId) return btn;
btn.selected = !btn.selected;
return btn;
});
setButtons(newButtons);
};
return (
<div>
<h1>Hello StackBlitz!</h1>
<div>
{buttons.map((bt) => (
<button
key={bt.id}
onClick={() => handleButton(bt.id)}
className={bt.selected ? 'buttonPressed' : 'button'}
>
{bt.title}
</button>
))}
</div>
</div>
);
}
I hope it helps.
Edit: the BUTTONS array was modified to add a selected property. Now several buttons can be selected at the same time.
I am trying to grab the user input on key pressed and pass it to that list above next. I feel like there must be a way to reset the state and make it persist, but I just can't figure it out? How can I understand this?
import { useState, useEffect, useRef, useMemo } from 'react';
import '../sign.css';
const VALUES = [
{ id: 1, label: "name", text: "Hi, What is your Name?", placeholder: "Enter your full name" },
{ id: 2, label: "uname", text: "What shall we call you?", placeholder: "Enter a username" },
{ id: 3, label: "email", text: "Enter you email", placeholder: "Email" },
{ id: 4, label: "password", text: "Choose a password", placeholder: "make sure you dont forget" },
{ id: 5, label: "signup", text: "sign up", placeholder: ""},
];
export default function SignUp() {
const [show, setShow] = useState(VALUES)
const [currentIndex, setCurrentIndex] = useState(0);
const [details, setDetails] = useState('');
useEffect(() => {
}, [show]);
const onKeyPressed = (ev, id) => {
if (ev.charCode === 13) {
ev.preventDefault();
const nextRender = currentIndex + 1;
if (nextRender < show.length) {
setCurrentIndex(nextRender);
setDetails(ev.target.value);
} else {
//todo
}
}
}
const displayItem = useMemo(() => show[currentIndex], [show, currentIndex]);
return (
<div className="container" id="container">
<div className="navigation">
<ol>
<li>{this should display their name}</li>
<li>{this should display their username}</li>
<li>{this should display their email}</li>
</ol>
</div>
<form id="sign-form" className="sign-form">
<ol className="questions">
{
<li onKeyPress={(KeyboardEvent) => onKeyPressed(KeyboardEvent, displayItem.id)} key={displayItem.id} >
<span><label htmlFor={displayItem.label}>{displayItem.text}</label></span>
<input id={displayItem.id} name={displayItem.label} type="text" placeholder={displayItem.placeholder} autoFocus/>
</li>
};
</ol>
</form>
</div>
)
Right, I think I know what you mean now. I've run the code in CodeSandbox and it makes sense. You want to do like a stepper for your registration where you ask a single question at a time.
Storing values in an object would still be a preferable way of doing this. But you need to get the label for the value and append it to the existing object. You can build your object with data when you go through your stepper.
Here is a working solution. Hopefully that's what you were looking for: https://codesandbox.io/s/unruffled-pasteur-76xux4?file=/src/App.js
I modified the onKeyPressed to grab the label from VALUES array based on the index we are currently on. Then that label is used as a key inside of the object where the value is the value from the event handler
const onKeyPressed = (ev, id) => {
if (ev.charCode === 13) {
ev.preventDefault();
const label = show[currentIndex].label; // grab the label based on the current index
const nextRender = currentIndex + 1;
if (nextRender < show.length) {
setCurrentIndex(nextRender);
setDetails({ ...details, [label]: ev.target.value }); // add the value to the details object where key is the label
} else {
//todo
}
}
};
I let you the code that it works like I think that you want
import { useState, useEffect, useRef, useMemo } from 'react';
const VALUES = [
{ id: 1, label: "name", text: "Hi, What is your Name?", placeholder: "Enter your full name" },
{ id: 2, label: "uname", text: "What shall we call you?", placeholder: "Enter a username" },
{ id: 3, label: "email", text: "Enter you email", placeholder: "Email" },
{ id: 4, label: "password", text: "Choose a password", placeholder: "make sure you dont forget" },
{ id: 5, label: "signup", text: "sign up", placeholder: ""},
];
export default function SignUp() {
const [show, setShow] = useState(VALUES)
const [currentIndex, setCurrentIndex] = useState(0);
const [details, setDetails] = useState({});
useEffect(() => {
}, [show]);
const onKeyPressed = ( ev, id ) => {
if (ev.charCode === 13) {
ev.preventDefault();
const nextRender = currentIndex + 1;
if (nextRender < show.length) {
setCurrentIndex(nextRender);
const label = VALUES[currentIndex].label;
details[label] = ev.target.value;
setDetails(details);
} else {
//todo
}
}
}
const displayItem = useMemo(() => show[currentIndex], [show, currentIndex]);
return (
<div className="container" id="container">
<div className="navigation">
<ol>
{Object.keys(details).map((key) => (
<li><a href="#" dataref={key}>{key}: {details[key]}</a></li>
))}
</ol>
</div>
<form id="sign-form" className="sign-form">
<ol className="questions">
<li onKeyPress={( KeyboardEvent ) => onKeyPressed(KeyboardEvent, displayItem.id)} key={displayItem.id}>
<span><label htmlFor={displayItem.label}>{displayItem.text}</label></span>
<input id={displayItem.id} name={displayItem.label} type="text" placeholder={displayItem.placeholder}
autoFocus/>
</li>
</ol>
</form>
</div>
)
}
I'm trying to update a State in my project based on a User Selection from a dropdown menu.
I passed the 2 states (roomType & occupants) along with their setter/change functions (onRoomTypeChange & onOccupantsChange) from the parent Component.
I’ve tried to read through the React Select Library, but having trouble.
Wondering if someone could point me in the right direction.
import React from 'react';
import Select from 'react-select';
const roomOptions = [
{ value: 'Standard', label: 'Standard' },
{ value: 'Delux', label: 'Delux' }
];
const occupantOptions = [
{ value: 1, label: '1' },
{ value: 2, label: '2' },
{ value: 3, label: '3' },
{ value: 4, label: '4' },
];
const RoomDetails = (props) => {
let {
roomType,
onRoomTypeChange,
occupants,
onOccupantsChange
} = props;
const handleChange = (e) => {
onRoomTypeChange(e.target.value);
};
return (
<div>
<div className="form-group">
<label className="select-label">Room Type: {roomType} </label>
<Select
// value={e.target.value}
onChange={handleChange}
options={roomOptions}
theme={theme}
/>
<label className="select-label">Guests: {occupants}</label>
<Select
value={occupants}
onDatachange={onOccupantsChange}
options={occupantOptions}
theme={theme}
/>
</div >
</div>
);
};
export default RoomDetails;
I resolved the and wanted to post an update:
I had installed another package called “react-dropdown-select” that I removed from my package.json, I believe was interfering with my react-select package .
The return object was not an event, it was the selected object from the corresponding options array. I destructured the value property in the parameter and used the Setter Function on the value only.
I originally included a Value= attribute on the Select Component, which was set to my current state value
<Select
value={stateProperty}
/>
When I removed the Value attribute, the bug was gone
(Here is the solution)
import React from 'react';
import Select from 'react-select';
const roomOptions = [
{ value: 'Standard', label: 'Standard' },
{ value: 'Delux', label: 'Delux' }
];
const occupantOptions = [
{ value: 1, label: '1' },
{ value: 2, label: '2' },
{ value: 3, label: '3' },
{ value: 4, label: '4' },
];
const RoomDetails = (props) => {
let {
onRoomTypeChange,
onOccupantsChange
} = props;
const handleChangeRoom = ({ value }) => {
console.log(value);
onRoomTypeChange(value);
};
const handleChangeOccupants = ({ value }) => {
console.log(value);
onOccupantsChange(value);
};
return (
<div>
<div className="form-group mb-2">
<span className="form-label mt-3">Room Type </span>
<Select
onChange={handleChangeRoom}
options={roomOptions}
theme={theme}
placeholder="Room Type"
/>
<span className="form-label mt-3">Guests</span>
<Select
onChange={handleChangeOccupants}
options={occupantOptions}
theme={theme}
placeholder="Number of Guests"
/>
</div >
</div>
);
};
export default RoomDetails;