I have object of items whitch i am mapping
const paymentMethods = [
{
name: 'Blik',
checked: true
},
{
name: 'Przelewy24',
checked: false
}
]
{paymentMethods.map((el) => (
<PaymentContainer>
<Label>{el.name}</Label>
<RadioButton type="radio" name="payment" value={el.name} onClick={(e) => handleSetPayment(e)} checked={el.checked} />
</PaymentContainer>
))}
My problem is when i passing checked property this way
I can't select one where checked value is default false
There should be a checked attribute use that.
const paymentMethods = [
{
name: 'Blik',
checked: true
},
{
name: 'Przelewy24',
checked: false
}
]
{paymentMethods.map((el,i) => (
<PaymentContainer key={i}>
<Label>{el.name}</Label>
<RadioButton type="radio" name={el.name} value={el.name} onClick={(e) => handleSetPayment(e)} checked={el.check}/>
</PaymentContainer>
))}
Related
I am having an app with two sections. Left section contains the categories and the right section containing the items under it. Under each category, I have the button to select all or unselect all items. I see the state changes happening in the code ( it is pretty printed inside HTML) but the checkbox values are not getting updated. Can someone help?
https://codesandbox.io/s/zealous-carson-dy46k8?file=/src/App.js
export const RightSection = ({ name, apps, json, setJson }) => {
function handleSelectAll(categoryName, type) {
const checked = type === "Select All" ? true : false;
const updated = Object.fromEntries(
Object.entries(json).map(([key, category]) => {
if (category.name !== categoryName) {
return [key, category];
}
const { name, tiles, ...rest } = category;
return [
key,
{
name,
...rest,
tiles: tiles.map((item) => ({
...item,
checked
}))
}
];
})
);
setJson(updated);
}
return (
<>
<div>
<input
type="button"
value={`select all under ${name}`}
onClick={() => handleSelectAll(name, "Select All")}
/>
<input
type="button"
value={`unselect all under ${name}`}
onClick={() => handleSelectAll(name, "Unselect All")}
/>
<h4 style={{ color: "blue" }}>{name} Items</h4>
{apps.map((app) => {
return (
<section key={app.tileName}>
<input checked={app.checked} type="checkbox" />
<span key={app.tileName}>{app.tileName}</span> <br />
</section>
);
})}
</div>
</>
);
};
import { useEffect, useState, useMemo } from "react";
import { SidebarItem } from "./SideBarItem";
import { RightSection } from "./RightSection";
import "./styles.css";
export default function App() {
const dummyJson = useMemo(() => {
return {
cat1: {
id: "cat1",
name: "Category 1",
tiles: [
{
tileName: "abc",
searchable: true,
checked: false
},
{
tileName: "def",
searchable: true,
checked: true
}
]
},
cat2: {
id: "cat2",
name: "Category 2",
tiles: [
{
tileName: "ab",
searchable: true,
checked: true
},
{
tileName: "xyz",
searchable: true,
checked: false
}
]
},
cat3: {
id: "cat3",
name: "Category 3",
tiles: [
{
tileName: "lmn",
searchable: true,
checked: true
},
{
tileName: "",
searchable: false,
checked: false
}
]
}
};
}, []);
const [json, setJson] = useState(dummyJson);
const [active, setActive] = useState(dummyJson["cat1"]);
return (
<>
<div className="container">
<div>
<ul>
{Object.values(json).map((details) => {
const { id, name } = details;
return (
<SidebarItem
key={name}
name={name}
{...{
isActive: id === active.id,
setActive: () => setActive(details)
}}
/>
);
})}
</ul>
</div>
<RightSection
name={active.name}
apps={active.tiles}
{...{ json, setJson }}
/>
</div>
<p>{JSON.stringify(json, null, 2)}</p>
</>
);
}
since you have not updated data of checkbox (in your code) / logic is wrong (in codesandbox) do the following add this function in RightSection
...
function setTick(app, value: boolean) {
app.checked = value;
setJson({...json})
}
...
and onChange in input checkbox
<input
onChange={({ target }) => setTick(app, target.checked)}
checked={app.checked}
type="checkbox"
/>
Codesandbox: see line 25 -> 28 and line 48 in RightSection.tsx are the lines I added
For the two buttons select all and unselect all to update the state of the checkboxes, the data must be synchronized (here you declare active as json independent of each other, this makes the update logic complicated. Unnecessarily complicated, please fix it to sync
const [json, setJson] = useState(dummyJson);
const [activeId, setActiveId] = useState('cat1');
const active = useMemo(() => json[activeId], [json, activeId]);
and update depends:
<SidebarItem
key={name}
name={name}
{...{
isActive: id === activeId,
setActive: () => setActiveId(id)
}}
/>
Codesandbox: line 60 -> 63 and line 81 -> 82 in file App.js
https://codesandbox.io/s/musing-rhodes-yp40fi
the handleOperation function could also be rewritten very succinctly but that is beyond the scope of the question
I implemented the form through react final form
const products= [
{ label: "T Shirt", value: "tshirt" },
{ label: "White Mug", value: "cup" },
{ label: "G-Shock", value: "watch" },
{ label: "Hawaiian Shorts", value: "shorts" },
];
<>
<Form
onSubmit={onSubmit}
render={({ handleSubmit, pristine, invalid, values }) => (
<form onSubmit={handleSubmit} className="p-5">
{products &&
products.map((product, idx) => (
<div className="custom-control custom-checkbox" key={idx}>
<Field
name="state"
component="input"
type="checkbox"
value={product.value}
/>
<label
className="custom-control-label"
htmlFor={`customCheck1-${product.value}`}
>
{product.label}
</label>
</div>
))}
<button type="submit" disabled={pristine || invalid}>
Submit
</button>
<pre>{JSON.stringify(values, 0, 2)}</pre>
</form>
)}
/>
</>
If I am selecting checkboxes the checked values are showing array of values like [tshirt,cup] but I need to show the array of objects like [ { label: "T Shirt", value: "tshirt" }, { label: "White Mug", value: "cup" }]
I tried so many ways but I have not any luck. Please help me to out of this problem
values will always be the array consisting of the "value" attribute for the Field tag.
If you want the object from the products array,you could do the following
console.log(values.map(val => products.find(p => p.value === val)))
or create an object first via reduce & then use it.
const obj =products.reduce((map,p)=>{
map[value]=p
return map
},{})
console.log(values.map(v => productMap[v]))
add a onchange method to you input. the method must take value of product.
const [selectedProducts, setSelectedProducts] = useState([]);
const handleChange = (value) =>{
const itemToAdd = products.find(product => product.value === value);
const index = selectedProducts.findIndex(item => item.value === value);
if (index === -1){
setSelectedProducts([...selectedProducts, products[index]])
}else {
const data = [...selectedProducts];
data.splice(index, 1);
setSelectedProducts(data);
}
}
some change to jsx
<Field
onChange = {handleChange}
name="state"
component="input"
type="checkbox"
value={product.value}
checked = {selectedProducts.findIndex(item => item.value === value)!== -1}
/>
I have this component:
import React from 'react';
const options = [
{ label: "Lifestyle", value: "lifestyle"},
{ label: "Area", value: "area" },
{ label: "Random", value: "random" }
];
const ChannelCategory = props =>
props.visible ? (
<div>
{props.title}
<ul>
{options.map((option) => (
<li key={option.value}>
<label>
{option.label}
<input
className={props.className}
name={props.name} // need to be different
selected={props.selected === option.value} // e.g. lifestyle === lifestyle
onChange={() => props.onChange(option.value)}
type="radio"
/>
</label>
</li>
))}
</ul>
</div>
) : null;
export default ChannelCategory;
I am rendering it on another page here in a .map:
let displayExistingChannels = null;
if (channels !== null){
displayExistingChannels = (
channels.map(channel => {
return (
<Grid key={channel.key} item style={styles.gridItem} justify="space-between">
<ChannelListItem
channel={channel}
isSaving={isSaving}
onDeleteChannelClick={onDeleteChannelClick}
key={channel.key}
onFormControlChange={onFormControlChange}
onUndoChannelClick={onUndoChannelClick}
/>
{channel.category}
<ChannelCategory
visible={true}
onChange={value => setCategoryName(value)}
title="Edit Category"
selected={channel.category}
name={channel.key} // unique for every channel
/>
</Grid>
)
})
)
}
I am using fake data for the map:
const fakeChannelData = setupChannels(
[{id: "2f469", name: "shopping ", readOnly: false, category: "lifestyle"},
{id: "bae96", name: "public", readOnly: true, category: "null"},
{id: "06ea6", name: "swimming ", readOnly: false, category: "sport"},
{id: "7e2bb", name: "comedy shows ", readOnly: false, category: "entertainment"}]);
const [channels, setChannels] = useState(fakeChannelData);
Please can someone tell me why when I add selected={channel.category} in my .map function it does not show the selected category preselected on the FE on page load? Not sure where I have gone wrong? Thanks!
checked is the correct attribute to use for input tag, not selected.
<input
...
checked={props.selected === option.value}
...
/>
ref: https://developer.mozilla.org/fr/docs/Web/HTML/Element/Input/radio
I have a list of chat room channels for people to talk i.e there is a lifestyle channel, shopping channel, pets channel etc.
I am now trying to categorise each channel to make it easier for the user to find what they want. In order to do so, on creation of a chatroom channel I need the user to select which category the channel they are creating best fits into. A bit like YouTube does when you upload a video.
So far I have created a separate component which is a list of checkboxes with the different categories the user can put their channel into:
import React from 'react';
const options = [
{ label: "Lifestyle", value: "lifestyle"},
{ label: "Area", value: "area" },
{ label: "Random", value: "random" },
{ label: "Comedy", value: "comedy" },
{ label: "Entertainment", value: "entertainment" }
];
const ChannelCategory = (props) => {
return (
<div>
{props.title}
<ul>
{options.map((option) => (
<li key={props.key}>
<label>
{option.label}
<input
className={props.className}
name="test"
checked={props.checked}
onChange={() => props.onChange(option.value)}
type="checkbox"
/>
</label>
</li>
))}
</ul>
</div>
)
};
export default ChannelCategory;
I am using the above component on the page below, I would like that when the user selects just ONE of the options only ONE input box is checked, however at the moment when I click ONE input box for instance lifestyle they ALLLL get checked and for every single channel too:( Any ideas why?
const [checked, setCheckBoxChecked] = useState(false);
[...]
const onAddCategory = (value) => {
console.log(value);
if (value === "lifestyle") {
setCheckBoxChecked(checked => !checked);
}
if (value === "area") {
setCheckBoxChecked(checked => !checked);
}
if (value === "random") {
setCheckBoxChecked(checked => !checked);
}
if (value === "comedy") {
setCheckBoxChecked(checked => !checked);
}
};
[...]
const options = [
{ label: "Lifestyle", value: "lifestyle"},
{ label: "Area", value: "area" },
{ label: "Random", value: "random" },
{ label: "Comedy", value: "comedy" },
{ label: "Entertainment", value: "entertainment" }
];
return (
<form noValidate autoComplete='off' onSubmit={onSubmit}>
<Card style={styles.card}>
<CardContent>
<Box padding={3}>
<FormLegend title={`${formTitle} (${channels.length})`} description={formDescription} />
<Box marginTop={3} width='50%'>
<Grid container direction='column' justify='flex-start' alignItems='stretch' spacing={1}>
{channels.map(channel => {
return (
<Grid key={channel.key} item style={styles.gridItem} justify="space-between">
<ChannelListItem
channel={channel}
isSaving={isSaving}
onDeleteChannelClick={onDeleteChannelClick}
key={channel.Key}
onFormControlChange={onFormControlChange}
onUndoChannelClick={onUndoChannelClick}
/>
<ChannelCategory
key={channel.key}
options={options}
onChange={value => onAddCategory(value)}
title="Add your chatroom to a category so that users can find it easily"
checked={checked}
/>
</Grid>
)
})}
[...]
</Grid>
</Grid>
</Box>
</Box>
</CardContent>
</Card>
</form>
);
Instead of storing true or false inside the checked variable, you should store the value inside of checked. Like this:
const onChangeAttribute = (value) => {
console.log(value);
setCheckBoxChecked(value);
};
And now while rendering the checkbox you should check if checked is equal to the name of that checkbox like this:
<input
className={props.className}
name={option.value}
checked={props.checked === option.value}
onChange={() => props.onChange(option.value)}
type="checkbox"
/>
This should resolve your issue.
Use an array to store all checked boxes and in your ChannelCategory check if the current value exists in the checked array then set checked to true for that checkbox. If you want to select only one category use radio buttons
const {useState, useEffect} = React;
const options = [
{ label: "Lifestyle", value: "lifestyle" },
{ label: "Area", value: "area" },
{ label: "Random", value: "random" },
{ label: "Comedy", value: "comedy" },
{ label: "Entertainment", value: "entertainment" }
];
const ChannelCategory = props => {
return (
<div>
{props.title}
<ul>
{props.options.map(option => (
<li key={props.key}>
<label>
{option.label}
<input
className={props.className}
name={option.value}
checked={props.checked.includes(option.value)}
onChange={e => props.onChange(e.target.checked, option.value)}
type="checkbox"
/>
</label>
</li>
))}
</ul>
</div>
);
};
function App() {
const [checked, setCheckBoxChecked] = useState([]);
const onAddCategory = (isChecked, value) => {
const temp = [...checked];
if (isChecked) {
temp.push(value);
setCheckBoxChecked(temp);
return;
}
setCheckBoxChecked(temp.filter(item => item !== value));
};
return (
<div className="App">
<ChannelCategory
key={"channel.key"}
options={options}
onChange={onAddCategory}
title="Add your chatroom to a category so that users can find it easily"
checked={checked}
/>
</div>
);
}
ReactDOM.render(
<App />,
document.getElementById("react")
);
<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="react"></div>
Radio buttons example
I'm using a document structure like this
render() {
return (
<div className="MyComponent">
<ul className="">
{parseRecommendations(this.props.recommendations)}
</ul>
</div>
);
}
function parseRecomendations(recommendations) {
return recommendations.map((recommendation, key) => {
return (<Recommendation data={recommendation} key={key} />);
});
}
Where each Recommendation is its own component containing a checkbox
class Recommendation extends Component {
const recommendation = this.props.data;
const pageUrl = recommendation.url;
return (
<li className="article-item" key={key}>
<div className="article-info">
<input type="checkbox" defaultChecked="checked" aria-described-by={recommendation.pii} />
<a className="journal-title" href={pageUrl} id={recommendation.pii}>{recommendation.title}</a>
</div>
</li>
);
I'd like to have a title saying [Download (x) PDFs], where x is the number of selected checkboxes. How do I find the value of x in this case?
You need to store information about whether input is "checked" in your data. Then, simply count items with truthy "checked" flag.
Here is my solution. You should be able to get principle here and modify your code.
const data = [
{ checked: false, value: 'document 1' },
{ checked: true, value: 'document 2' },
{ checked: true, value: 'document 3' },
{ checked: false, value: 'document 4' },
{ checked: false, value: 'document 5' },
];
const Item = props => (
<div>
<input type="checkbox" checked={props.checked} onChange={props.onCheckChange} />
{ props.value }
</div>
)
var Hello = React.createClass({
getInitialState() {
return {
items: this.props.items.concat(),
};
},
onCheckChange(idx) {
return () => {
const items = this.state.items.concat();
items[idx].checked = !items[idx].checked;
this.setState({items});
}
},
totalChecked() {
return this.state.items.filter(props => props.checked).length;
},
render() {
return (
<div>
{ this.state.items.map((props, idx) => (
<Item {...props} key={idx} onCheckChange={this.onCheckChange(idx)} />
)) }
Total checked: { this.totalChecked() }
</div>
);
}
});
ReactDOM.render(
<Hello items={data} />,
document.getElementById('container')
);
If you just want to get the number of selected check-boxes you can try this
let checkedBoxes = document.querySelectorAll('input[name=chkBox]:checked');
Then get the total checked boxes via checkedBoxes.length
Edit:
Instead of querying whole document. You can get the nearest possible parent via getElementsByClassName or getElementById and then apply querySelectorAll on that element.
e.g
let elem = document.getElementsByClassName("MyComponent");
let checkedBoxes = elem.querySelectorAll('input[name=chkBox]:checked');
You also could obtain the total of selected checkboxes by element type. The "console.log(totalSelectedCheckboxes)" will print them when the state of totalSelectedCheckboxes change using useEffect Hook.
import React, { useState, useEffect } from 'react';
const RenderCheckboxes = () => {
const [totalSelectedCheckboxes, setTotalSelectedCheckboxes] = useState(0);
function handleChk() {
setTotalSelectedCheckboxes(document.querySelectorAll('input[type=checkbox]:checked').length);
}
useEffect(() => {
console.log(totalSelectedCheckboxes);
}, [totalSelectedCheckboxes]);
return (<div>
<div>
<input type="checkbox" value={1} onChange={() => handleChk()} />Chk1
</div>
<div>
<input type="checkbox" value={2} onChange={() => handleChk()} />Chk2
</div>
<div>
<input type="checkbox" value={2} onChange={() => handleChk()} />Chk2
</div>
</div>);
}
export default RenderCheckboxes;