@sinevik

Как решить проблему в React.js c checkbox?

z29826va.beget.tech

Обратите внимание, если выбрать марку, после марки выбрать несколько моделей, а затем выбрать другую марку- то оно сохраняет выбранные checked у checkbox. Но почему? Как решить эту проблему?

77fa0904d58f49f434a48fa2b184b0f1.png

Вот эти 2 компонента

main

import React from "react";
import Model from "./model";
import Sortcost from "./sortcost";
import Marka from "./marka";
import Body from "./body";
import Auto from "./auto";
import { BrowserRouter as Router, Route, Link } from "react-router-dom";
import RouterDom from "react-router-dom";



class Main extends React.Component {
	constructor(props){
		super(props);
			this.state = {
					WeatherObj:null,
					mark:[],
					model:[],
					body:[],
					page:1,
					valueinputfrom:"",
					valueinputbefore:"",
					content:{
						display:"flex",
						flexDirection:"row"
					},
					stylecontainer:{
						display:"flex",
						flexDirection:"column"
					}
			}
		}
	
	changemark(e){
		let unit = e.target.value;
		let arr = this.state.mark;
		let arrtwo = this.state.model;
		if(e.target.value === "all"){
			arr.length = 0;
			arrtwo.length = 0;
			this.setState({mark: arr});
			this.setState({model: arrtwo});
			this.setState({page: 1});
		}else{
			arr.length=0;
	 		arr.push(unit);
	 		arrtwo.length = 0;
	 		this.setState({model: arrtwo});
	 		this.setState({mark: arr});	
	 		this.setState({page: 1});
		}
		
	
	}

	changemodel(e){
		let unit = e.target.value;
		let arr = this.state.model;
	 	if(e.currentTarget.checked === true){
	 		arr.push(unit);
	 		this.setState({model: arr});
	 		this.setState({page: 1});
	 	}else{
	 		arr.splice(arr.indexOf(unit), 1);
	 		this.setState({model: arr});
	 		this.setState({page: 1});
	 	}
	
	}
	changebody(e){
		let unit = e.target.value;
		let arr = this.state.body;
	 	if(e.currentTarget.checked === true){
	 		arr.push(unit);
	 		this.setState({body: arr});
	 		this.setState({page: 1});
	 	}else{
	 		arr.splice(arr.indexOf(unit), 1);
	 		this.setState({body: arr});
	 		this.setState({page: 1});
	 	}
	
	}

	valueinputfrom(e) {
		let text = e.target.value;
		this.setState({valueinputfrom: text});
		this.setState({page: 1});
	}

	valueinputbefore(e) {
		let text = e.target.value;
		this.setState({valueinputbefore: text});
		this.setState({page: 1});
	}
	page(e) {
		let text = e.currentTarget.id;
		this.setState({page: text});
	}




	
	render() {

      	return(
    <div style={this.state.content}>
      	<div style={this.state.stylecontainer}>
      		<div>
      			<Marka changemark={this.changemark.bind(this)}/>
      		</div>
      		<div>
      			<Model mark={this.state.mark} changemodel={this.changemodel.bind(this)} />
      		</div>
      		<div>
      			<Body changebody={this.changebody.bind(this)}/>
      		</div>
      		<div>
      			<Sortcost meaningfrom={this.state.valueinputfrom} meaningbefore={this.state.valueinputbefore} from={this.valueinputfrom.bind(this)} before={this.valueinputbefore.bind(this)}  />
      		</div>
      	</div>
      	<div>
      		<div>
      			<Auto pagedata={this.state.page} page={this.page.bind(this)} mark={this.state.mark} model={this.state.model} body={this.state.body} from={this.state.valueinputfrom}  before={this.state.valueinputbefore} />
      		</div>
      	</div>
    </div>
      	
      	)
   	
	

	}

}
export default Main;


Model

import React from "react";
import { BrowserRouter as Router, Route, Link } from "react-router-dom";
import RouterDom from "react-router-dom";


class Model extends React.Component {
	constructor(props){
		super(props);
			this.state = {
					mark:props.mark,
					stylelabel:{
						font: "15px Arial",
					},
					header:{
						font: "15px Arial",
						marginTop:"40px",
						marginBottom:0,

					},
					signature:{
						marginBottom: 0
					},

					stylecontainerinput:{
						display:"flex",
						flexDirection:"column",
						width:"100px",
						background:"#848482",
						borderRadius: "6px"
					}
				}
		}
	

	render() {
		let result = null;
		let header = null;
		if(this.state.mark.length > 0){
			header = <p style={this.state.header}>Модель</p>
			result = this.state.mark.map( (item, index)  => {
				if (item == "bmw") return (
						<div style={this.state.stylecontainerinput} key={index}>
							<label style={this.state.signature}>{item}</label>
							<div>
							<input id="X1" value="X1" type="checkbox" onClick={this.props.changemodel}/>
      						<label style={this.state.stylelabel} htmlFor="X1">X1</label>
      						</div>
      						<div>
      						<input id="X5" value="X5" type="checkbox" onClick={this.props.changemodel}/>
      						<label style={this.state.stylelabel} htmlFor="X5">X5</label>
      						</div>

      						<div>
      						<input id="X6" value="X6" type="checkbox" onClick={this.props.changemodel}/>
      						<label style={this.state.stylelabel} htmlFor="X6">X6</label>
      						</div>
      						
      						
						</div>

					)
				if (item == "audi") return (
						<div style={this.state.stylecontainerinput} key={index}>
							<label style={this.state.signature}>{item}</label>
							<div>
							<input id="A3" value="A3" type="checkbox" onClick={this.props.changemodel}/>
      						<label style={this.state.stylelabel} htmlFor="A3">A3</label>
      						</div>
      						<div>
      						<input id="A4" value="A4" type="checkbox" onClick={this.props.changemodel}/>
      						<label style={this.state.stylelabel} htmlFor="A4">A4</label>
      						</div>

      						<div>
      						<input id="A7" value="A7" type="checkbox" onClick={this.props.changemodel}/>
      						<label style={this.state.stylelabel} htmlFor="A7">A7</label>
      						</div>
      						
						</div>

					)
				if (item == "ford") return (
						<div style={this.state.stylecontainerinput} key={index}>
							<label style={this.state.signature}>{item}</label>
							<div>
							<input id="Mondeo" value="Mondeo" type="checkbox" onClick={this.props.changemodel}/>
      						<label style={this.state.stylelabel} htmlFor="Mondeo">Mondeo</label>
      						</div>
      						<div>
      						<input id="Focus" value="Focus" type="checkbox" onClick={this.props.changemodel}/>
      						<label style={this.state.stylelabel} htmlFor="Focus">Focus</label>
      						</div>
      						<div>
      						<input id="Kuga" value="Kuga" type="checkbox" onClick={this.props.changemodel}/>
      						<label style={this.state.stylelabel} htmlFor="A7">Kuga</label>
      						</div>	
						</div>

					)
				if (item == "mazda") return (
						<div style={this.state.stylecontainerinput} key={index}>
							<label style={this.state.signature}>{item}</label>
							<div>
							<input id="323" value="323" type="checkbox" onClick={this.props.changemodel}/>
      						<label style={this.state.stylelabel} htmlFor="323">323</label>
      						</div>

      						<div>
      						<input id="626" value="626" type="checkbox" onClick={this.props.changemodel}/>
      						<label style={this.state.stylelabel} htmlFor="626">626</label>
      						</div>

      						<div>
      						<input id="3" value="3" type="checkbox" onClick={this.props.changemodel}/>
      						<label style={this.state.stylelabel} htmlFor="3">3</label>
      						</div>
      						
      						
						</div>

					)

			});
		}
		return (
		<div>
			{header}
			{result}
		</div>
		)

	}

		
}

export default Model;
  • Вопрос задан
  • 654 просмотра
Решения вопроса 1
rockon404
@rockon404 Куратор тега React
Frontend Developer
Причина бага в том, что вы рендерите списки моделей через map и передаете в key индекс, а индекс в вашем случае всегда равен 0. Само решение показывать списки таким способом очень плохое.
Как можно переделать. Во-первых использовать для mark не массив, а строку. Использование массива для значения mark необоснованно. Во-вторых все модели автомобилей с фильтров хранить в объекте вида:
const allCars = {
  bmw: [
    { name: 'x1',  ... },
     ...
  ],
  ...
}

Тогда:
render() {
  const { mark } = this.props;
  const models = allCars[mark]; 
  
  return (
    <div>
      {mark && (
        <div>
          <label>{mark}</label>
          {models.map(model => (
            <div key={model.name}>
              <label>{model.name}</label>
              <input
                name="model"
                value={model.name}
                type="checkbox"
                onClick={this.props.changemodel}
              />
            </div>
          ))}
        <div>
      )}
    </div>
  );
}

Менять состояние так как это делаете вы нельзя, так как вызов setState асинхронный. Например код метода changeMark можно переписать так:
changeMark (e) {
  const { value } = e.target;

  this.setState({ 
    mark: value === 'all' ? [] : [...prevState.mark, value],
    model: [],
    page: 1,
  });
  }
}

Для изменения состояния на основе предыдущего передавайте в setState функцию возвращающую состояние, на момент вызова первым аргументом(prevState в примере) в нее придет текущее состояние компонента.

Для переменных значение которых не переопределяется правильней использовать const, а не let.

Хандлеры changeBody и changeModel можно объединить в один, а использовав xor из lodash сократить до такого вида:
handleCheckboxCheck(e) {
    const { name, value } = e.target;

    this.setState(prevState => ({
      [name]: _.xor(prevState[name], +value), 
    }));
}


Черную магию с array.push и array.length = 0 для обновления состояния, забудьте и никогда не вспоминайте.

Метод render это самое худшее место для бинда хандлера. Антипатерн. Так можно делать только если надо передать аргументы.
Вместо этого бинд лучше сделать в конструкторе:
constructor(props) {
  super(props);
  this.changeModel = this.changeModel.bind(this);
}

Либо переписав хандлер с метода класса в поле класса:
метод класса:
changeModel (e) {
    // some actions
}

поле класса:
changeModel = e => {
  // some actions
};
Ответ написан
Пригласить эксперта
Ваш ответ на вопрос

Войдите, чтобы написать ответ

Войти через центр авторизации
Похожие вопросы