How to change value of all checkboxes in react? - javascript

I want to change value of my checkboxes from true to false
var x = document.getElementsByClassName("checkbox")
for(let i=0; i<=x.length; i++) {
x[i].checked = false;
}
but when I try to do this I get an error: Cannot set properties of undefined (setting 'checked') because x is HTMLCollection and x[i] returns only
<input type="checkbox" class="checkbox">
so no 'checked' property.
How can I handle this?

Please either change the <= to < in the for loop
for(let i=0; i<x.length; i++) {}
Or make it to loop till length - 1
for(let i=0; i<=x.length - 1; i++) {}

The issue with how you're doing it is that you're not making use of the states in React.
This is how I would do it.
Set initial state of the checkboxes:
constructor(props) {
super(props);
this.state = {checked : false};
}
then pass the state into the checked property of the input:
<input type="checkbox" checked={this.state.checked} onChange={(e) => this.handleChange(e)} />
In the handleChange function update the state of the checkbox:
handleChange = (e) => {
this.setState({ checked: e.target.checked })
}
This should update all the checkboxes that have checked={this.state.checked}

Try this:
<input type="checkbox" checked={this.state.chkbox} onChange={this.handleChangeChk} />
Or you can check this question here briefly explained:
Set checkbox value in React JS

The problem is in the for loop. The condition should be i<x.length only.
var x = document.getElementsByClassName("checkbox")
for (let i = 0; i < x.length; i++) {
x[i].checked = false;
}
// below for testing purpose
for (let i = 0; i < x.length; i++) {
console.log(x[i].checked)
}
<input type="checkbox" class="checkbox">

It's best to use setState to manage the checkbox values - best to update the values here in the ShadowDOM as this part of what makes React is all about
Specific instructions on how to implement checkboxes and react can be found here --
http://react.tips/checkboxes-in-react-16/

If this is code in an React application as you're suggesting you shouldn't be mixing native element methods and React JS like that as React has its own way of particular way of updating the DOM.
You should store the status of the checkboxes in state, set their checked status to that state, and then call a function to update the state causing a re-render with that new state.
const { useState } = React;
function Example() {
const [ state, setState ] = useState(true);
function handleClick() {
setState(false);
}
return (
<div>
<input type="checkbox" checked={state} />
<input type="checkbox" checked={state} />
<input type="checkbox" checked={state} />
<input type="checkbox" checked={state} />
<button onClick={handleClick}>Uncheck the boxes!</button>
</div>
);
};
// Render it
ReactDOM.render(
<Example />,
document.getElementById("react")
);
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/17.0.1/umd/react.production.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/17.0.1/umd/react-dom.production.min.js"></script>
<div id="react"></div>

Related

Cannot read the array property. Objects are not valid as a React child

I am following a tutorial exercise and I got the following error
Objects are not valid as a React child
I know this error is related to the object as I am trying to access the object but it needs an individual item of an object but not sure.
Why cannot the map loop over each item in the array?
Following is my code
var template = <h1>Indecision App</h1>;
var app = {
title: 'Indecision App',
subtitle: 'yo',
options: []
}
let count = 0;
function checkSubtitles (subtitle){
if(subtitle){
return <p>{subtitle}</p>
}else{
return undefined
}
}
function reset(){
count = 0;
reRenderApp();
}
function increaseCount(){
count++;
reRenderApp();
}
function onSubmitHandle(e){
e.preventDefault();
const options = e.target.elements.options;
app.options.push(options);
reRenderApp();
e.target.elements.options.value = ''
}
function removeAll(){
app.options = [];
reRenderApp();
}
function reRenderApp(){
var templateTwo = (
<div>
<h1>{app.title}</h1>
{checkSubtitles(app.subtitle)}
<p>Count: {count}</p>
<p>Array Length: {app.options.length > 0 ? app.options.length : '0 Items'}</p>
<ol>
{app.options.map((item)=>{
return <li key={item}>{item}</li>
})}
</ol>
<hr></hr>
<form onSubmit={onSubmitHandle}>
<input type="text" name="options" />
<input type="submit" value="Push to the Array" />
<input type="reset" value="Empty my list" onClick={removeAll} />
</form>
<button onClick={()=>{
increaseCount();
}}>Increase Count</button>
<button onClick={()=>{
reset();
}}>Reset Count</button>
</div>
)
ReactDOM.render(templateTwo, appRoot)
}
var appRoot = document.getElementById('app');
reRenderApp();
<body>
<div id="app"></div>
<script src="https://unpkg.com/react#16.0.0/umd/react.development.js"></script>
<script src="https://unpkg.com/react-dom#16.0.0/umd/react-dom.development.js"></script>
<script src="./app.js"></script>
</body>
</html>
The main problem is, as you mentioned: Objects are not valid as a React child
But, what is happening?
If we go into:
function onSubmitHandle(e){
e.preventDefault();
// Line 1
const options = e.target.elements.options;
app.options.push(options);
reRenderApp();
// Line 2
e.target.elements.options.value = ''
}
So in Line 1, you're pushing options into the options array.
But, then in Line 2, we can notice options has an attribute (so, it's an object)
So, if you change Line 1, from:
const options = e.target.elements.options;
To this:
const options = e.target.elements.options.value;
It'd work.
Also, to check what I'm saying you have 2 options:
option 1: console.log
function onSubmitHandle(e){
e.preventDefault();
const options = e.target.elements.options;
console.log({ options })
app.options.push(options);
reRenderApp();
e.target.elements.options.value = ''
}
option 2: make that option a valid child of react with JSON.stringify()
<ol>
{app.options.map((item, index)=>{
return <li key={index}>{JSON.stringify(item)}</li>
})}
</ol>
You can do
{app.options.length && app.options.map((item)=>{
return <li key={item}>{item}</li>
})}
But you must be sure that "item" here is not an object as you can't render an object
The reason for this is that your options array is going to be filled with elements as you're pushing the input element with the name of "option" into your array - this elements are objects in JS which you can't render out as list items.
Use React State to store anything that's going to change in the UI - in this case your list of options> So rather than doing
var app = {
title: 'Indecision App',
subtitle: 'yo',
options: []
}
let count = 0;
Do:
const [options, setOptions] = React.useState([]);
const [count, setCount] = React.useState(0);
Title and subtitle are probably not going to change, so just put them in h1 & h2 elements - if they are, then use the state pattern again.
Get rid of the two inputs with types of "submit" & "reset" just use normal button elements instead.
You'll also need an onchange event on your input where the text will go in and each time the onchange event is fired (i.e, when a user types) you'll need to save the input text
const [inputText, setInputText] = React.useState('');
const handleChange = (e) => {
const {value} = e.target;
setInputText(value)
}
<input type="text" value={inputText} onChange={handleChange}/>
Then in your onHandleSubmit function, just have
const onHandleSubmit = () => {
setOptions([...options, inputText]);
setInputText('')
}
This should work

Loop through checkboxes and conditionally disable unchecked

I'm trying to write a basic function in pure JS that simply checks the number of checked checkboxes, and if that number exceeds a certain amount, disables the rest. I can achieve this easily in jQuery, but trying to get it working in pure JS. I have a CodePen set up here and I'm including my working JS below. Thanks for any insight here.
(function() {
var checkboxes = document.querySelectorAll('input[id^="mktoCheckbox"]');
var active = document.querySelectorAll('input[id^="mktoCheckbox"]:checked');
var numActive = active.length;
console.log(numActive);
if (numActive > 1) {
for(var i = 0; i < checkboxes.length; i++){
if (checkboxes[i].checked == true) {
return;
} else {
checkboxes[i].disabled == true;
}
}
}
})();
There are two problems in your code:
You're returning from the if condition, which causes the loop to terminate.
You're not assigning true to disabled attribute, instead you're comparing with ==.
Change the related snippet to the following to make it work:
if (numActive > 1) {
for (var i = 0; i < checkboxes.length; i++) {
if (checkboxes[i].checked != true) {
checkboxes[i].disabled = true;
}
}
}
You can find a working fork of your pen here. Following is a working SO snippet:
(function() {
var checkboxes = document.querySelectorAll('input[id^="mktoCheckbox"]');
var active = document.querySelectorAll('input[id^="mktoCheckbox"]:checked');
var numActive = active.length;
console.log(numActive);
if (numActive > 1) {
for (var i = 0; i < checkboxes.length; i++) {
if (checkboxes[i].checked != true) {
checkboxes[i].disabled = true;
}
}
}
})();
<fieldset>
<legend>Choose some monster features</legend>
<div>
<input type="checkbox" id="mktoCheckbox_1" name="feature" value="scales" checked />
<label for="scales">Scales</label>
</div>
<div>
<input type="checkbox" id="mktoCheckbox_2" name="feature" value="horns" />
<label for="horns">Horns</label>
</div>
<div>
<input type="checkbox" id="mktoCheckbox_3" name="feature" value="claws" />
<label for="claws">Claws</label>
</div>
<div>
<input type="checkbox" id="mktoCheckbox_4" name="feature" value="tails" checked />
<label for="tails">Tails</label>
</div>
</fieldset>
Here's a working pen.
First of all, imo, you should use getAttribute and setAttribute on DOM elements. HTMLInputElement.checked afaik only reflects the checked attribute (and converts it to boolean) for convenience.
With that in mind, you need to now test against strings with the strict comparison operator ===. E.g.
if(checkbox.getAttribute('checked') === 'true') {...}
since using == would evaluate to false
true == 'true' // false
Also, instead of the for loop you can make use the for-of loop on DOM collections. I.e. this works:
const checkboxes = document.querySelectorAll('...');
for(const checkbox of checkboxes) {
...
}
Keep in mind that you use continue and break to control the loop and not return.

generate unique array base on checkbox value

I have few checkbox, what I want is to build a unique array list base on the checkbox value, if the checkbox is checked, push it to the list, if the checked checkbox is already in the list, remove it from the list.
http://jsbin.com/bojinudelo/edit?js,console,output
What's the problem of my code below?
generateFilter = (e) => {
let temp = [];
temp = this.state.arr;
if(temp.indexOf(this.state.arr) > -1){
delete temp[e.target.name]
}else{
temp.push(e.target.name);
}
this.setState({arr:temp})
console.log(this.state.arr)
}
render() {
return (
<div>
<input onChange={this.generateFilter} type="checkbox" name="apple"/>
<input onChange={this.generateFilter} type="checkbox" name="samsung"/>
</div>
);
}
Reason is, you are checking the index of wrong item, Instead of checking the index of this.state.arr, check the index of e.target.value, as well as deleting in a wrong way.
temp is an array not an Object, so instead of using delete, you need to use the splice and pass the index of item.
Use this function:
generateFilter = (e) => {
let temp = this.state.arr.slice(),
index = temp.indexOf(e.target.name);
if(index != -1){
temp.splice(index, 1);
}else{
temp.push(e.target.name);
}
this.setState({arr:temp})
console.log(temp)
}
Check the working code: http://jsbin.com/tatakamobu/1/edit?js,console,output
Run this snippet:
class HelloWorldComponent extends React.Component {
constructor(props){
super(props);
this.state = {
arr: []
}
}
generateFilter = (e) => {
let temp = this.state.arr.slice(),
index = temp.indexOf(e.target.name);
if(index != -1){
temp.splice(index, 1);
}else{
temp.push(e.target.name);
}
this.setState({arr:temp})
console.log(temp)
}
render() {
return (
<div>
<input onChange={this.generateFilter} type="checkbox" name="apple"/>
<input onChange={this.generateFilter} type="checkbox" name="samsung"/>
</div>
);
}
}
ReactDOM.render(
<HelloWorldComponent />,
document.getElementById('react_example')
);
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/15.1.0/react.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/15.1.0/react-dom.min.js"></script>
<div id='react_example'/>

Checkboxes not working in react js

my check boxes are not getting checked, when created dynamically. I am not able to find the problem. Though, when I hard-code the values for check box id and label for, it just works.
var category_list = this.props.categories_list.map(function(name, i) {
// debugger
return (
<div className="group-chkbx list-group-item">
<input key={i+11} type="checkbox" id={name.category_id} name="category" />
<label htmlFor={name.category_id}>{name.name}</label>
</div>
)
});
After a lot of research one of my colleague helped me out with a solution. The htmlFor and id must be same, but cannot be only numeric. The Ids that I'm using are purely numeric. When I added alphabet as a prefix, it just started working like charm. Thanks all for showing interest and helping out here.
There's nothing that would set the checked prop on them, anyway. When should they be checked?
(Also, remember that components in arrays (such as what .map returns) should have unique key props.)
If your checkboxes are not getting checked, most probably is that some other functionality is preventing it.
Here and example of how to get the checkbox values:
class WithChecks extends Component {
constructor(props){
super(props);
this.getValue = this.getValue.bind(this);
}
getValue(e){
const chk = e.target;
console.log(chk.checked);
console.log(chk.value);
}
render() {
const arr = ['a', 'b', 'c', 'd'];
return (
<div>
{
arr.map((value, index) => {
return (
<div key={index}>
<input type="checkbox"
id={'chk' + index}
onChange={this.getValue}
name="category"
value={value} />
<label htmlFor={'chk' + index}>{value}</label>
</div>
);
})
}
</div>
);
}
}
Maybe this can help to clarify.
The checked property of the input will control whether it is checked. Usually I use local state (or something from global redux state to control what is checked). Little Example:
class Something extends React.Component {
constructor(props) {
super(props);
this.state = {
checked: 0
}
this.handleChange = this.handleChange.bind(this);
}
handleChange(e) {
// Do Stuff
}
render() {
return (
<div>
{
this.props.categories_list.map(function(name, i) {
return (
<div className="group-chkbx list-group-item" key={i}>
<input checked={i === this.state.checked} onChange={this.handleChange} type="checkbox" id={name.category_id} name="category" />
<label htmlFor={name.category_id}>{name.name}</label>
</div>
)
});
}
</div>
);
}
}

Why does react function in ReactClass get called repeatedly on form input change events

I have a react-bootstrap React Class, where the createList function gets called on every key input to either of the form inputs (workDone, or hoursAndMinutes). I'm new to reactjs, and perhaps this is normal behavior, but it seems to me that it isn't, and hence I'm doing something wrong.
var SubjectBox = React.createClass({
getInitialState(){
return({
totalHoursAndMinute:0,
subject:'',
workDone:'',
hoursAndMinutes:'',
})
},
dropDownSelected:function(e){
this.setState({subject:e.target.value})
},
handleChangeToWorkDone(){
let s = this.refs.workDone.getValue();
console.log(s);
this.setState({
workDone: s
});
},
validateWorkDone:function(){
let length = this.state.workDone.length;
if (length >= 10) return 'success';
else if (length > 5) return 'warning';
else if (length > 0) return 'error';
},
validateHoursAndMinutes(){
let hm = this.state.hoursAndMinutes.split(':');
if (hm.length === 2){
return 'success';
}else{
return 'error';
}
},
handleChangeToHoursMinute(){
var minutes =0;
let s =this.refs.hoursAndMinutes.getValue();
let hm = s.split(':');
if (hm.length===2){
var h = parseInt(hm[0].trim());
var m = parseInt(hm[1].trim());
if (!m.isNaN){
var minutes = h*60+m;
}
}
this.setState({
hoursAndMinutes: s,
totalMinutes:minutes
});
},
createList: function(){
console.log("create list function.");
var list=[];
for (var i = 0; i < this.props.subjects.length;i++){
list.push(<option key={i} value={this.props.subjects[i].subject}>{this.props.subjects[i].subject}</option>)
}
return list;
},
handleSubmit: function(e){
e.preventDefault();
console.log(this.state.workDone);
console.log(this.state.subject);
},
render(){
return(
<form onSubmit={this.handleSubmit}>
<Input ref="subjectList" type="select" label="Subject" onChange={this.dropDownSelected}>
{this.createList()}
</Input>
<Input ref="workDone"
type="text"
value={this.state.workDone}
placeholder="What did you do?"
label="What did you do" help="Input is 10 or more characters."
bsStyle={this.validateWorkDone()} hasFeedback
groupClassName="group-class" labelClassName="label-class"
onChange={this.handleChangeToWorkDone} />
<Input ref="hoursAndMinutes"
type="text" value={this.state.hoursAndMinutes} placeholder="HH:MM?"
label="How long did you do it?" help="Input in hours:minutes, example 1:5 = an hour and five minutes."
bsStyle={this.validateHoursAndMinutes()} hasFeedback
groupClassName="group-class"
labelClassName="label-class" onChange={this.handleChangeToHoursMinute} />
<Button type="submit" bsStyle="success">Submit</Button>
</form>
)
}
});
It happens because you are using in handleChangeToWorkDone and handleChangeToWorkDone setState which calls re-render
setState() will always trigger a re-render unless conditional
rendering logic is implemented in shouldComponentUpdate(). If mutable
objects are being used and the logic cannot be implemented in
shouldComponentUpdate(), calling setState() only when the new state
differs from the previous state will avoid unnecessary re-renders.
In React Js, the HTML/DOM is always just a representation on the State of your React Component.
Whenever there is a onChange or onBlur or any event, if the State of the React Component is changed(using setState func), the ReactJs component is re-rendered(using Render func).
Only when the State of the React Js component changes, your UI can be updated with the value you key in. Hence, It is the expected behaviour in React Js.

Categories