简体   繁体   中英

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)

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM