I have an array called initialList the array contains three items these items have their own checkboxes, when I check on any of the items I want to push them into the list Array and at the same time remove them from the initialList array, I don't know what to write in the clickHandler function and how to render the list array
import React, { useState } from 'react'
function CheckBox3() {
const initialList = [{id: 1, name:'jim'},{id: 2, name: 'joe'},{id: 3, name: 'beth'}]
const [list, setList] = useState([])
const clickhandler = () => {
}
return (
<div>
<ol>
{initialList.map((item) => (
<li key={item.id}>{item.name}
<input type="checkbox" onClick={clickhandler} />
</li>))}
</ol>
</div>
)
}
export default CheckBox3
Good morning Neel.
if checkbox - there is only 2 possible values - boolean false/true.
So it's good idea to use negation here.
then U use onChange instead onClick:
<input type="checkbox" onChange={() => setChecked(!checked)}/>
I suggest to hold the actual checkbox value in state:
const [checked, setChecked] = useState()
Also if You have x checkboxes - there is need also for holding, which id of checkbox is in which state.
Or - just add checked parameter to your initialList object.
[
{id:1 checked: false}
{id:2 checked: true}
]
And also - for identifier You can add i to your map function - and in check function - reverse the value of i - id :
{initialList.map((item, i) => (
<li key={item.id}>{item.name}
<input type="checkbox" onClick={(i) =>check(i)} />
</li>))}
I recommend you to keep the list in the componente state, add/update the checked property every time an item is clicked:
function CheckBox3() {
const initialList = [{id: 1, name:'jim'},{id: 2, name: 'joe'},{id: 3, name: 'beth'}]
const [list, setList] = useState(initialList);
const clickhandler = ({target}) => {
const {checked, id} = target;
setList(prev => {
const clickedItem = prev.find(item => item.id.toString() === id);
clickedItem["checked"] = checked;
return [...prev];
});
}
return (
<div>
<ol>
{initialList.map((item) => (
<li key={item.id}>{item.name}
<input type="checkbox" id={item.id} onClick={clickhandler} />
</li>))}
</ol>
</div>
)
}
Then you can filter the selected items:
const selected = list.filter(item => item.checked);
This works perfectly for what you are trying to achieve.
What I did was I check if the checkbox is checked and if it's then push it to the list array and check if it item exists in the initialList and if it does too it should filter it out.
function CheckBox3() {
const [initialList, setIinitialList] = useState([{id: 1, name:'jim'},{id: 2, name: 'joe'},{id: 3, name: 'beth'}])
const [list, setList] = useState([])
const clickHandler = (e, item) => {
if (e.target.checked) {
setList(prevState => [
...prevState,
item
])
const newInitialList = initialList.filter((list, index) => list.id !== item.id);
setIinitialList(newInitialList)
}
}
useEffect(() => {
// Perform whatever you want to do when list changes here
console.log(list);
}, [list])
return (
<div>
<ol>
{initialList.map((item) => (
<li key={item.id}>{item.name}
<input type="checkbox" onClick={(e) => clickHandler(e, item)} />
</li>))}
</ol>
</div>
);
}
Related
I'm trying to display a number of array of radio buttons generated dynamically. So far, I can select each button in the corresponding component and display the selected value, but when I'm trying to add the values I simply can't find the right solution.
const Dropdown = () => {
const customers = [{ group1: 4 }, { group2: 2 }];
// const [arr, setArr] = useState([]);
// const [total, setTotal] = useState(0);
const RadioButtons = ({ item }) => {
const [selectedButton, setSelectedButton] = useState(0);
const onButtonChange = (e) => {
setSelectedButton(e.target.value);
// const chosenButton = Object.keys(item)[0];
// const updatedArr = arr.map((entry) => {
// if (entry.chosenButton === chosenButton) {
// return { chosenButton, value: e.target.value };
// }
// return entry;
// });
// if (!updatedArr.find((entry) => entry.chosenButton === chosenButton)) {
// updatedArr.push({ chosenButton, value: e.target.value });
// }
// setArr(updatedArr);
// setTotal(updatedArr.reduce((sum, entry) => sum + entry.value, ""));
};
const arrayLength = Number(Object.values(item)) + 1;
return (
<div className="group-buttons">
<p>Selected value:{selectedButton}</p>
<div className="buttons">
{Array.from({ length: arrayLength }, (_, i) => i).map((index) => (
<div key={index}>
<input
label={index}
type="radio"
name={`${Object.keys(item)}${arrayLength}`}
value={index}
onChange={onButtonChange}
checked={selectedButton === `${index}`}
/>
</div>
))}
</div>
</div>
);
};
const radioButtonsArray = customers.map((item) => {
return (
<li key={Object.keys(item)}>
<div className="buttons">
<RadioButtons item={item} />
</div>
</li>
);
});
return (
<ul>
<form>{radioButtonsArray}</form>
{/* <p>Selected values: {total}</p> */}
</ul>
);
};
When removing the commented section, I'm able to display the last selected numbers combined, but for some reason the buttons don't appear to be checked and {selectedButton} no longer changes value.
Here is a screenshot of how the radio buttons are displayed, with the value of selectedButton changing. image of code with commented lines
And here is how it looks with the modifications:
image of code with uncommented lines
I would like to know if there is a way to achieve this just using React
The problem with the code below is that the items start showing up when i click then in the filter, what i want to do is have all the items available and then when i start filtering have only the ones show that are filtered. Should i solve this in the function or in the component with some conditional?
// Data
const data = [
{
id: 0,
poging: "Stoep",
},
{
id: 1,
poging: "Bon",
},
{
id: 2,
poging: "Bal",
},
];
// Filter function
function Filters() {
const [items, setItems] = useState(data);
const [filters, setFilters] = useState([]);
const clickFunction = (e) => {
const listItem = e.target.parentNode;
const list = listItem.parentNode;
// Loop through filter items
const filterList = Array.from(list.querySelectorAll("li"));
// Create new array based on the checked filters and change filter state
const newArray = filterList
.filter((item) => item.querySelector("input:checked"))
.map((item) => item.querySelector("label").textContent.toLowerCase());
setFilters(newArray);
};
// Filter list
<ul onChange={(e) => clickFunction(e)}>
<li>
<label htmlFor="stoep">Stoep</label>
<input type="checkbox" name="" id="stoep" />
</li>
<li>
<label htmlFor="bon">Bon</label>
<input type="checkbox" name="" id="bon" />
</li>
<li>
<label htmlFor="bal">Bal</label>
<input type="checkbox" name="" id="bal" />
</li>
</ul>
// Output of the items
<ul>
{items
.filter((item) => filters.includes(item.poging.toLowerCase()))
.map((item) => {
return (
<li key={item.id}>
<div>{item.poging}</div>
</li>
);
})}
</ul>
that seems pretty easy - just change this:
.filter((item) => filters.length === 0 || filters.includes(item.poging.toLowerCase()))
I have stomped on a problem that i don't know how to resolve.
I have a react-select input that passes a selected value to another select input.
When a user clicks on submit button then i have to display an array of every selector input that the user has selected as a list of items and then the form should reset all select values.
For this, i have tried submitting to an array but it only shows one item from all selectors and form doesn't reset its values.
Here is a sandbox link
https://codesandbox.io/s/awesome-carson-i99g8p?file=/src/App.js
How can I archive this I have tried everything but I could not figure out how can i archive this functionality.
Ok. First tricky thing of react-select is, the value you assign to the component must be an object with label and valueproperties, not just the value. Meaning, when handling change events, you should set the state using the full event object.
This is the code mostly fixed:
import React, { useState, useEffect } from "react";
import Select from "react-select";
const options = [
{ value: "0", label: "0" },
{ value: "1", label: "1" },
{ value: "2", label: "2" }
];
const options2 = [
{ value: "Before Due Date", label: "Before Due Date" },
{ value: "After Due Date", label: "After Due Date" }
];
const App = (props) => {
const [numOfDays, setNumOfDays] = useState('');
const [beforeDueDate, setBeforeDueDate] = useState('');
const [items, setItems] = useState([]);
const [reminders, setReminders] = useState([]);
const submit = (event) => {
event.preventDefault(); // <-- prevent form submit action
const obj = {};
obj.id = reminders.length + 1;
obj.numOfDays = numOfDays.value;
obj.beforeDueDate = beforeDueDate.value;
setReminders((items) => [...items, obj]); // <-- update arr state
setBeforeDueDate("");
setNumOfDays("");
};
function numOfDaysHandle(event) {
// const numOfDays = event.value;
setNumOfDays(event);
setItems((items) => [...items, items]);
}
function beforeDueDateHandle(event) {
// const value = event.value;
setBeforeDueDate(event);
}
const removeReminder = (id) => {
setReminders(reminders.filter((item) => item.id !== id));
};
return (
<>
<form>
<div>
{reminders.map((item, index) => (
<div key={index}>
<div>
<span>
{item.numOfDays} days {item.beforeDueDate}
</span>
<button onClick={() => removeReminder(item.id)}>
removeItem
</button>
</div>
</div>
))}
</div>
<div>
<Select
options={options}
value={numOfDays}
id="numOfDays"
placeholder="Days"
isSearchable={false}
//onChange={numOfDaysHandle}
onChange={numOfDaysHandle}
/>
<Select
options={options2}
value={beforeDueDate}
id="beforeDueDate"
placeholder="Before Due Date"
isSearchable={false}
onChange={beforeDueDateHandle}
/>
</div>
{items.map((item, index) => (
<div key={index}>
<Select
options={options}
value={item.numOfDays}
id="numOfDays"
placeholder="Days"
isSearchable={false}
onChange={numOfDaysHandle}
/>
<Select
options={options2}
value={item.beforeDueDate}
id="beforeDueDate"
placeholder="Before Due Date"
isSearchable={false}
onChange={beforeDueDateHandle}
// onClick={() => setItems((items) => [...items, items])}
/>
</div>
))}
<button
onClick={submit}
//disabled={!numOfDays}
>
Set Reminder
</button>
</form>
</>
);
};
export default App;
Please see if you can move forward now, I could not understand what you want exactly with the array of <Select /> elements.
I want to when the user clicks submit button it should add new object. with the given value which is stored in list varaible.It should not delete the old objects,only name Property should be added.
import { useState } from "react";
export default function Add() {
const [todo, addTodo] = useState([{ name: "cat", time: "2 minutes ago" }]);
const [list, setList] = useState();
const add = () => {
addTodo({...todo, todo: {
name: list
}})
};
return (
<div>
<input
type="text"
value={list}
onChange={(e) => setList(e.target.value)}
/>
<button onClick={add}>Add new</button>
{todo.map((item, i) => (
<li key={i}>
{item.name}
<span style={{ marginLeft: "15px" }}>{item.time}</span>
</li>
))}
</div>
);
}
When your state update is dependent on the previous state value ,its a good practice to make the state updater to have an inline function .
addTodo(existingTodos => ([...existingTodos, { name: list}] ))
Here we are saying , get the exisitingTodos list and append the new todo along with the exisitingTodos .
const add = () => {
addTodo(existingTodos => ([...existingTodos, { name: list}] ))
};
Because todo is an array so you should pass an array when call addTodo
addTodo([
...todo,
{
name: list,
},
]);
I have 7 checkboxes that are rendered within a map method.. each checkbox has a question and when all checkboxes are checked, a button should be activated.. can someone please tell me how can I validate this using useState() hook.. I know how to do this with one checkbox, but I don't know how to handle multiple checkboxes that are rendered within a map method. A code example should be very helpful. Any help will be appreciated.
const [isChecked, setIsChecked] = useState(false);
{ questions.map(q => (
<input
type={q.radio ? "radio" : "checkbox"}
onClick={() => setIsChecked(!isChecked)}
value={isChecked}
id={option.id}
value={option.value}
name={`${q.name}`}
/>
There are 7 checkboxes rendered with this map method. How should I handle the state in this case?
If your questions are dynamic list you can use below approach:
const QUESTIONS = ["Do you use Stackoverflow?", "Do you asked a question?"];
function Confirmation({ data }) {
const [questions, setQuestions] = React.useState(
data.map((question, index) => {
return { id: index, text: question, checked: false };
})
);
const handleClick = (id, checked) => {
const newQuestions = [...questions];
const index = newQuestions.findIndex((q) => q.id === id);
newQuestions[index].checked = checked;
setQuestions(newQuestions);
};
const isButtonDisabled = questions.some((q) => q.checked === false);
return (
<div>
{questions.map((question) => (
<React.Fragment>
<input
key={question.id}
name={`question-${question.id}`}
type="checkbox"
checked={question.checked}
onClick={(e) => handleClick(question.id, e.target.checked)}
/>
<label htmlFor={`question-${question.id}`}>{question.text}</label>
</React.Fragment>
))}
<button disabled={isButtonDisabled}>Confirm</button>
</div>
);
}
ReactDOM.render(<Confirmation data={QUESTIONS} />, document.getElementById("root"));
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/16.8.4/umd/react.production.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/16.8.4/umd/react-dom.production.min.js"></script>
<div id="root"></div>
To add to thedude's answer, you can dynamically adjust the checked array based on changes to your questions array so that you don't have to manually maintain the initial states or remember to sync it to the new length of questions if it changes.
const [checked, setChecked] = useState([])
const arrLength = questions.length;
useEffect(() => {
setChecked(() => (
Array(arrLength).fill().map((_, i) => checked[i] || false)
));
}, [arrLength]);
Following your example of mapping over your questions.
You could load those questions in useState and append the check value to them.
Click the Run Code Snippet below to see it working.
const { useState, useEffect } = React;
const App = props => {
const { questions } = props;
const [state, setState] = useState(null);
const onChangeCheckbox = index => event => {
const newState = [...state];
newState[index].checked = !newState[index].checked;
setState(newState);
}
useEffect(() => {
setState(questions.map(i => ({...i, checked: false }) ));
}, [questions]);
// Not loaded yet
if (!state) return <div></div>;
return <div>
{state && state.length > 0 && <div>
{state.map((i, k) => <p key={`question-${k}`}>
<label><input onChange={onChangeCheckbox(k)} type="checkbox" name={i.name} checked={i.checked} /> {i.question}</label>
</p>)}
</div>}
<hr />
<p><small>Debug</small></p>
<pre><code>{JSON.stringify(state, null, ' ')}</code></pre>
</div>;
};
const questions = [
{
name: 'cyborg',
question: 'Are you a cyborg?'
},
{
name: 'earth',
question: 'Do you live on earth?'
},
{
name: 'alien',
question: 'Are you from another planet?'
}
];
ReactDOM.render(<App questions={questions} />, document.querySelector('#root'));
body {
font-family: Arial, sans-serif;
}
pre {
background: #efefef;
padding: 20px;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/16.8.4/umd/react.production.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/16.8.4/umd/react-dom.production.min.js"></script>
<div id="root"></div>
Use the key param to give each checkbox an identifier so it can be modified.
const [isChecked, setIsChecked] = useState([])
useEffect(() => {
compareList()
})
{ questions.map((q,i) => (
<input
type={q.radio ? "radio" : "checkbox"}
onClick={() => setIsChecked(!isChecked[i])}
value={isChecked[i]}
id={option.id}
value={option.value}
name={`${q.name}`}
key=i
/>
If you use controlled input the solution might be like this:
const initialState = [
{
name: "Check option 1",
checked: false
},
{
name: "Check option 2",
checked: false
},
]
const App = () => {
const [checkboxes, setCheckboxes] = useState(initialState)
const [quizFinished, setQuizFinished] = useState(false)
// the useEffect checks if all questions are completed
useEffect(()=>{
setQuizFinished(checkboxes.reduce((acc, cbx) => acc && cbx.checked, true))
}, [checkboxes])
function handleChange(e) {
setCheckboxes(
checkboxes.map(cbx => cbx.name === e.target.name ? {...cbx, checked:!cbx.checked} : cbx)
)
}
return (
<>
{checkboxes.map(cbx => <Checkbox name={cbx.name} checked={cbx.checked} onChange={handleChange}/>)}
{quizFinished && <button>Finish</button>}
</>
)
}
const Checkbox = ({name, checked, onChange}) => {
return (
<>
<label>
{name}
<input type="checkbox" name={name} checked={checked} onChange={onChange} />
</label>
<br/>
</>
)
}
You can model your state to fit your usecase. For a list of 7 checkboxes you can have an array with 7 values:
const [checked, setChecked] = useState([false, false, false, false, false, false, false])
The when a check box is clicked:
{ questions.map((q, i) => (
<input
type={q.radio ? "radio" : "checkbox"}
onClick={() => setChecked(current => {
const newState = [...current]
newState[i] = !newState[i]
return newState
} )}
value={checked[i]}
id={option.id}
value={option.value}
name={`${q.name}`}
/>
To know if all are checked you can defined a variable:
const areAllChecked = checked.every(Boolean)