Mapping Nested Checkbox not working ReactJS - javascript

I have a function which triggers children checkboxes once main checkbox is checked, and all these checkboxes are mapped from JSON. The main checkboxes (Highest order) and all of its children checkboxes (2nd order) under them are shown on check and its working great, what i am trying to show is the children of those children of the main checkboxes (3rd order).
Basically to show all three orders under each other on check, and add the 3rd order to my current code, so Options Group shows Options, and under Options is what i want to show, which are Option 1, Option 2, option 3 and so on..
The checkbox values are passed as props from Checkbox.js to Itemlist.js where the fetch/map happens.
As I was trying to achieve this, I have been able to show each of these 3rd order checkboxes but each one alone instead of showing them as nested checkboxes. I've tried to include it in the existing mapping function to show them all as nested checkboxes as mentioned but it couldn't work it only works if it was mapped alone instead of the current mapped path which is const selectedItem. while the mapping path of the targeted checkboxes (3rd level) are added in Itemlist.js as const selectedMod,
Main Snippet : https://codesandbox.io/embed/6jykwp3x6n?fontsize=14
What I reached for so far to show the targeted checkboxes but individually: https://codesandbox.io/embed/o932z4yr6y?fontsize=14
Checkbox.js
import React from "react";
import "./Checkbox.css";
class Checkboxes extends React.Component {
constructor(props) {
super(props);
this.state = {
currentData: 0,
limit: 2,
checked: false
};
}
selectData(id, event) {
let isSelected = event.currentTarget.checked;
if (isSelected) {
if (this.state.currentData < this.props.max) {
this.setState({ currentData: this.state.currentData + 1 });
} else {
event.preventDefault();
event.currentTarget.checked = false;
}
} else {
if (this.state.currentData >= this.props.min) {
this.setState({ currentData: this.state.currentData - 1 });
} else {
event.preventDefault();
event.currentTarget.checked = true;
}
}
}
render() {
const input2Checkboxes =
this.props.options &&
this.props.options.map(item => {
return (
<div className="inputGroup2">
{" "}
<div className="inputGroup">
<input
id={this.props.childk + (item.name || item.description)}
name="checkbox"
type="checkbox"
onChange={this.selectData.bind(
this,
this.props.childk + (item.name || item.description)
)}
/>
<label
htmlFor={this.props.childk + (item.name || item.description)}
>
{item.name || item.description}{" "}
</label>
</div>
</div>
);
});
return (
<form className="form">
<div>
{/** <h2>{this.props.title}</h2>*/}
<div className="inputGroup">
<input
id={this.props.childk + this.props.name}
name="checkbox"
type="checkbox"
checked={this.state.checked}
onChange={this.selectData.bind(
this,
this.props.childk + this.props.uniq
)}
onChange={() => {
this.setState({
checked: !this.state.checked,
currentData: 0
});
}}
/>
<label htmlFor={this.props.childk + this.props.name}>
{this.props.name}{" "}
</label>
</div>{" "}
{this.state.checked ? input2Checkboxes : undefined}
</div>
</form>
);
}
}
export default Checkboxes;
Itemlist.js Where the mapping function happen
...
const selectedItem =
selectedChild.children && selectedChild.children.length
? selectedChild.children[this.state.itemSelected]
: null;
...
<div>
{selectedItem &&
selectedItem.children &&
selectedItem.children.map((item, index) => (
<Checkboxes
key={index}
name={item.name || item.description}
myKey={index}
options={item.children}
childk={item.id}
max={item.max}
min={item.min}
/>
))}
</div>
...

Related

React-Table, disable all checkboxes after checking first 5 checkboxes?

Using Indeterminate Checkboxes.I need to disable all the checkboxes dynamically when the threshold of selected checkboxes reaches to 5(any of the five in table).
function IndeterminateCheckbox({
indeterminate,
className = "",
setActionsVisible,
...rest
}: { indeterminate?: boolean } & HTMLProps<HTMLInputElement>) {
const ref = React.useRef<HTMLInputElement>(null!);
React.useEffect(() => {
if (typeof indeterminate === "boolean") {
setActionsVisible(indeterminate)
ref.current.indeterminate = !rest.checked && indeterminate;
}
}, [ref, indeterminate]);
return (
<input
type="checkbox"
ref={ref}
className={className + " cursor-pointer"}
{...rest}
/>
);
}
And the code/logic for column cell is,
cell: ({ row, getValue }) => {
return (
<div className="custom-checkbox">
<IndeterminateCheckbox
disabled={
row.getAllCells()[5].getValue() === "Cancelled"
}
{...{
checked:
row.getIsSelected() &&
row.getAllCells()[5].getValue() !== "Cancelled" &&
row.getAllCells()[5].getValue() !== "Completed" &&
row.getAllCells()[5].getValue() !== "Failed",
indeterminate: row.getIsSomeSelected(),
setActionsVisible: setActionsVisible,
onChange: row.getToggleSelectedHandler(),
className: "custom-control-input",
}}
/>
</div>
);
},
footer: (props) => props.column.id,
Can anybody suggest me where i am going wrong.? Thanks in Advance.
I am able to disable checkboxes on initial condition(eg. disabling all the checkboxes whose status is "Completed"). ButI need to disable all the checkboxes dynamically when the threshold of selected checkboxes reaches to 5.

Handle populating state from multiple checkboxes

I've got a form which builds a list of checkboxes from some data:
<fieldset className="visibility">
<div className="input-container checkbox">
<span className="label">Visible to</span>
<ul>
{
allForces.map(force => {
if (force.name !== 'White' && force.name !== currentMarkerForce) {
return (
<li key={force.uniqid}>
<label>
<input onChange={handleVisibilityChange} name={`visibility-${_.kebabCase(force.name)}`} type="checkbox" value={force.name} checked={markerVisibleTo.includes(force.name) }/>
{force.name} cell
</label>
</li>
)
}
})
}
</ul>
</div>
</fieldset>
As it is usually at least 2 items that will appear and can be checked, I wrote a handler for it which populates an array before posting back to the state, the contents of this array is initially populated from the existing state:
const visibilityChecked = [...markerVisibleTo]
const handleVisibilityChange = ({ target }) => {
const { checked, value } = target
checked ? visibilityChecked.push(value) : visibilityChecked.pop(value)
setMarkerVisibleTo(visibilityChecked)
}
The last line is a call to a useState hook this, mostly works but sometimes I get an odd behaviour where the wrong checkbox is selected:
Can anyone please help shed some light on what is causing this problem?
I may guess that happens because state update is asynchronous and by the time you attempt to apply changes with setMarkerVisibleTo() your state is different from the one you assume it is, you may try to put const visibilityChecked = [...markerVisibleTo] into handleVisibilityChange() body:
const handleVisibilityChange = ({ target }) => {
const visibilityChecked = [...markerVisibleTo]
const { checked, value } = target
checked ? visibilityChecked.push(value) : visibilityChecked.pop(value)
setMarkerVisibleTo(visibilityChecked)
}
Or, as I would write that:
const handleVisibilityChange = ({target:{checked,value}}) => {
const visibilityChecked = checked ?
[...markerVisibleTo, value] :
[...markerVisibleTo].filter(val => val != value)
setMarkerVisibleTo(visibilityChecked)
}
You may find full-blown demo over here:
//dependencies
const { render } = ReactDOM,
{ useState } = React
//mocking source data
const checkItems = [...'abcd']
//check list component
const CheckList = ({items}) => {
const [visibleMarkers, setVisibleMarkers] = useState(checkItems),
onVisibilityChange = ({target:{checked,value}}) => {
const visibilityChecked = checked ?
[...visibleMarkers, value] :
[...visibleMarkers].filter(val => val != value)
setVisibleMarkers(visibilityChecked)
}
return (
<div>
<ul>
{
items.map((item,key) => (
<li {...{key}}>
<label>
Option {item}
<input
type="checkbox"
value={item}
checked={visibleMarkers.includes(item)}
onChange={onVisibilityChange}
/>
</label>
</li>
))
}
</ul>
<span>visibleMarkers: {JSON.stringify(visibleMarkers)}</span>
</div>
)
}
//render
render (
<CheckList items={checkItems} />,
document.getElementById('root')
)
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/16.12.0/umd/react.production.min.js"></script><script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/16.11.0/umd/react-dom.production.min.js"></script><div id="root"></div>

How can I display dynamic state in react

I create a state dynamically, but I need use this state in my render.
See my issue:
handleChange = (name, checked) => {
this.setState({
[name]: checked,
})
}
So, in my render how can I display my [name] state? (I don't know his name, since it's dynamic)
render(){
console.log(this.state....?)
return(
<p>Hello</p>
)
}
My function handleChange is called when I checked my checbkox. So, if I have 5, 10, 20 checkboxes how can I display my [name] state?
----> UPDATE - FULL CODE <----
I'm using a hover propriety to display my checkbox:
CSS using material ui:
hideCheckbox: {
display: 'none',
},
showCheckbox: {
display: 'initial',
},
My main class:
export class Item extends Component {
state = {
isHovering: true,
checkboxChecked: false,
}
handleGetCardSelected = (id, checked) => {
//Here I set isHovering to display my checkbox
//checkboxChecked is a control variable to always display the checkbox if it's checked
if(checked){
this.setState({
isHovering: !this.state.isHovering,
checkboxChecked: true,
})
} else {
this.setState({
checkboxChecked: false,
})
}
}
handleMouseHover = () => {
if(!this.state.checkboxChecked){
this.setState(this.toggleHoverState);
}
}
toggleHoverState = (state) => {
return {
isHovering: !state.isHovering,
};
}
return(
<div
onMouseEnter={this.handleMouseHover}
onMouseLeave={this.handleMouseHover}
>
<div className={`
${this.state.isHovering && classes.hideCheckbox }
${this.state.checkboxChecked && classes.showCheckbox}
`}>
<CheckBoxCard handleGetCardSelected={this.handleGetCardSelected}/>
</div>
</div>
<div
onMouseEnter={this.handleMouseHover}
onMouseLeave={this.handleMouseHover}
>
<div className={`
${this.state.isHovering && classes.hideCheckbox }
${this.state.checkboxChecked && classes.showCheckbox}
`}>
<CheckBoxCard handleGetCardSelected={this.handleGetCardSelected}/>
</div>
</div>
<div
onMouseEnter={this.handleMouseHover}
onMouseLeave={this.handleMouseHover}
>
<div className={`
${this.state.isHovering && classes.hideCheckbox }
${this.state.checkboxChecked && classes.showCheckbox}
`}>
<CheckBoxCard handleGetCardSelected={this.handleGetCardSelected}/>
</div>
</div>
)
}
My CheckboxCard:
import React from 'react';
import { makeStyles, withStyles } from '#material-ui/core/styles';
import { Checkbox } from '#material-ui/core';
const GreenCheckbox = withStyles({
root: {
color: '#43a04754',
'&$checked': {
color: '#43a047',
},
'&:hover': {
color: '#43a047',
backgroundColor: 'initial',
},
},
checked: {},
})(props => <Checkbox color="default" {...props} />);
export default function CheckBoxCard(props){
const [state, setState] = React.useState({
idItem: false,
});
const handleCheckbox = name => event => {
setState({ ...state, [name]: event.target.checked });
let id = name
let checked = event.target.checked
props.handleGetCardSelected(id, checked)
};
return(
<GreenCheckbox
checked={state.idItem}
onChange={handleCheckbox('idItem')}
value="idItem"
inputProps={{
'aria-label': 'primary checkbox',
}}
/>
);
}
By default, my checkbox is hidden because my state isHovering is true, so the css variable hideCheckbox ('display: none') is set.
If I hover the element, is called handleMouseHover and the checkbox is displayed!
If I checked my checkbox, is set checkboxChecked for true and now I'm always displaying my checkbox! But, if I've two or more elements, all checkbox is displayed, because checkboxChecked is an unique element!
So, my checkboxChecked must be dynamic and per item! In this way, if is checked, only this checkbox will be displayed. The others no!
For first element: ${this.state.checkboxCheckedITEM1 && classes.showCheckbox}
For second element: ${this.state.checkboxCheckedITEM2 && classes.showCheckbox}
For second element: ${this.state.checkboxCheckedITEM3 && classes.showCheckbox}
I update my code in sandbox: https://codesandbox.io/embed/material-demo-16t91?fontsize=14
How can I do that?
So, if I have 5, 10, 20 checkboxes how can I display my [name] state?
I think the smart way to do this is to nest components. Have an abstract parent component, that only renders the child component once the name is available. By using this pattern your problem can be solved.
Check out this helpful article
You can filter your object to get only checked names.
Object.keys(this.state).filter(name => this.state[name]);
If you have a finite list of checkbox component
render() {
return (
<div>
<Checkbox
handleChange={() => this.handleChange('abc', !this.state[abc])}
className={this.state['abc'] ? 'checked-class' : '' }
/>
</div>
)
}
In another situation whereby you render a list of dynamic checkbox.
componentDidMount() {
//Calling API to return dynamic list of checkboxes, and dump them into a state
this.setState({ listOfCheckboxes });
}
Then in your render method, you will be using .map function
render() {
return (
<div>
{
this.state.listOfCheckboxes.map(x => (
<Checkbox
handleChange={() => this.handleChange(x.name, !this.state[x.name]) }
className={this.state[x.name] ? 'checked-class' : '' }
/>))
}
</div>
)
}
By doing so :
this.state[name]
You can look at Object.keys(), but if in future you would need to handle more than one state, iteration does not seem to solve the problem.
You could also give it a prefix [name_${name}]: checked, but would not recommend that at all.
If it is not a problem, use an object and you will have full controll over it.
this.setState({
something: {
name,
checked
},
})

How to remove unchecked items from cart state in React

I have items displayed with check boxes and once clicked/checked they are added to the cart. When unchecked they are added to the cart again. I am not sure how to toggle the checked to remove the item when unchecked.
export class DestinationPrices extends React.Component {
constructor(props) {
super(props);
this.state = {
flightData: [],
isChecked: props.isChecked || false,
data: "",
cart: []
};
this.handleCheckboxChange = this.handleCheckboxChange.bind(this);
}
// ...
}
Select(FD) {
return (
<div>
{FD.FlightID}
<label>
<Checkbox
id={FD.FlightID}
name={FD.FlightID}
value={this.state.isChecked}
onChange={() => this.handleCheckboxChange(FD)}
/>
<span>Select</span>
</label>
</div>
);
}
handleCheckboxChange = id => {
const cartItem = this.state.cart.filter(x => x.FlightID === id.FlightID);
this.setState({
isChecked: !this.state.isChecked,
cart: [...this.state.cart, id]
});
}
When the item is checked, it displays in the cart with some details.
With every click of the checkbox, the item adds to state and shows in the card, regardless if it is checked or unchecked. I need to delete the item from state if the item is unchecked.
Do I check if the item is already there and delete from cart?
render() {
var data = this.props.cart;
var arry = [];
arry.push(data);
return (
<div>
<Card body>
<CardTitle>
<h3>
<b>Flights Selected : </b>
</h3>
</CardTitle>
<CardText>
<span className="fa fa-cart-plus fa-2x"> {data.length} </span>{" "}
{this.getData()}
</CardText>
<div />
</Card>
</div>
);
}
Any help would be greatly appreciated, thanks!
One way of going about it is to do how you outlined in your question: check if the item already is in the cart. If it is, you can remove it from the cart. It it's not, you can add it to the cart.
handleCheckboxChange = item => {
this.setState(prevState => {
const isItemInCart = prevState.cart.some(el => el.FlightID === item.FlightID);
const cart = isItemInCart
? prevState.cart.filter(el => el.FlightID !== item.FlightID)
: [...prevState.cart, item];
return { cart };
});
}

React nested components with checkboxes

I have a website that contains more than a dozen components. Each parent component contains about 5 other components. Each 5 components contains a form. Parent components has 3 checkboxes. 1: Expand all components in that offer (expand all), 2: assign everything in that component (assign all), 3: Expand the component (expand product). Each child component has two checkboxes.1: Expand the component (also expand product), 2: assign everything from the component (also assign all). All of these options works on a state change. They all work correctly. The problem is that when I click parent checkbox, I can not deselect the child's single component. So I can not uncheck the checkbox inside child's component (child's expand product or child's assign all)
closed components
expand product on parent
expand product in parent, expand product in child
expand all in parent,triggers expand product in parent and it triggers expand product in childs component
here's first file that renders parent component :
class Offer extends React.Component {
constructor(props){
super(props);
this.state = {
checkedItems: new Map()
};
this.handleChange = this.handleChange.bind(this);
}
handleChange(e) {
const item = e.target.name;
const isChecked = e.target.checked;
this.setState(prevState => ({
checkedItems: prevState.checkedItems.set(item, isChecked)
}));
}
render(){
const { data, country_offer, country_offer_tab, countries, } = this.props;
return (
<div className="offer-wrapper">
<div className={"offer-header " + data[2] }>
<div>
<div className="custom_checkbox">
<div className="custom_checkbox">
<input
type={"checkbox"}
name={`${data[2]}_expand_all`}
className="expand_all"
checked={this.state.checkedItems.get(`${data[2]}_expand_all`) || false}
onChange={this.handleChange}
id={`${data[2]}_expand_all`}
/>
<label htmlFor={`${data[2]}_expand_all`}>Expand all</label>
</div>
<div className="custom_checkbox">
<input
type={"checkbox"}
name={`${data[2]}_assign_all`}
className="assign_all"
checked={this.state.checkedItems.get(`${data[2]}_assign_all`) || false}
onChange={this.handleChange}
id={`${data[2]}_assign_all`}
/>
<label htmlFor={`${data[2]}_assign_all`}>Assign all products</label>
</div>
<div className="custom_checkbox">
<input
type={"checkbox"}
name={`${data[2]}_expand_product`}
className="expand_product"
checked={( this.state.checkedItems.get(`${data[2]}_expand_product`) || this.state.checkedItems.get(`${data[2]}_expand_all`) ) || false}
onChange={this.handleChange}
id={`${data[2]}_expand_product`}
/>
<label htmlFor={`${data[2]}_expand_product`}>Expand product</label>
</div>
</div>
</div>
</div>
{
(this.state.checkedItems.get(`${data[2]}_expand_all`) || this.state.checkedItems.get(`${data[2]}`+'_expand_product')) &&
<CountryOffer
country_offer_tab={country_offer_tab}
countries={countries}
data={data}
expand_all={this.state.checkedItems.get(`${data[2]}_expand_all`)}
expand_product={this.state.checkedItems.get(`${data[2]}_expand_product`)}
assign_all={this.state.checkedItems.get(`${data[2]}_assign_all`)}
/>
}
</div>
)
}
}
export default Offer;
and here is child component
class CountryOffer extends React.Component{
constructor(props){
super(props);
this.state = {
checkedItems: new Map(),
};
this.handleChange = this.handleChange.bind(this);
}
handleChange(e) {
if (!this.props.expand_all){
const item = e.target.name;
const isChecked = e.target.checked;
this.setState(prevState => ({
checkedItems: prevState.checkedItems.set(item, isChecked)
}));
} else {
console.log('cant uncheck/check this')
}
}
render(){
const { country_offer_tab, countries, data, expand_all, expand_product, assign_all} = this.props;
return (
<div className={"offer-details " + data[2]}>
{country_offer_tab[data[2]].map((country, k) => {
return (
<div key={k}>
<div className="offer-country-header" >
<div>
<span className={"iti-flag "+(countries[k].symbol).toLowerCase()}/>
<span>{countries[k].name}</span>
</div>
<div className='custom_checkbox'>
<input
checked={assign_all ? assign_all : (this.state.checkedItems.get(`intersections_for ${data[2]}|${countries[k].id}`) || false)} onChange={this.handleChange}
type="checkbox"
name={"intersections_for "+ data[2] + '|' + countries[k].id}
id={"intersections_for "+ data[2] + '|' + countries[k].id}/>
<label className="intersections_group_label" htmlFor={"intersections_for "+ data[2] + '|' + countries[k].id}>Check/uncheck all</label>
</div>
<div className='custom_checkbox'>
<input
checked={expand_all ? expand_all : (this.state.checkedItems.get(`expand_geo ${data[2]}|${countries[k].id}`) || false)} onChange={this.handleChange}
type="checkbox"
name={"expand_geo " + data[2] + '|' + countries[k].id}
id={"expand_geo " + data[2] + '|' + countries[k].id}/>
<label htmlFor={"expand_geo "+ data[2] + '|' + countries[k].id}>Expand GEO</label>
</div>
</div>
{
(expand_all || this.state.checkedItems.get(`expand_geo ${data[2]}|${countries[k].id}`)) &&
<CountryOfferIntersections
country_offer_tab={country_offer_tab}
offer_id={data[2]}
country_id={countries[k].id}
check_all={this.state.checkedItems.get(`intersections_for ${data[2]}|${countries[k].id}`)}
assign_all={assign_all}
expand_product={expand_product}
/>
}
</div>
)
})}
</div>
)}
}
export default CountryOffer;
I will be grateful for any help
If true, the assign_all prop overrides any local state in CountryOffer
checked={assign_all ? assign_all : ...
This is a problem with not having a single source of truth. I would refactor your code so that CountryOffer is a stateless component, and keep all state in the parent Offer component.
Alternatively lifting management of shared state out of the components altogether. (For example with react context, redux, rxjs)

Categories

Resources