Как добавить свое состояние чекбоксу React
Решил изучить реакт, и вот споткнулся...
Пример того, как я пытался это сделать
class FormUserItem extends React.Component {
constructor(props) {
super(props);
// Эта привязка обязательна для работы `this` в колбэке.
this.handleChecked = this.handleChecked.bind(this);
}
handleChecked(e) {
this.props.onChange(e.target);
}
render() {
let className = "user__item";
if (this.props.isChecked) {
className += " active";
}
return (
<label>
<input
className={className}
name={"usr_"+this.props.id}
type="checkbox"
checked={ this.props.isChecked}
onChange={this.handleChecked}
/>
{this.props.name}
</label>
);
}
}
class FormUserItems extends React.Component {
constructor(props) {
super(props);
this.state = { isChecked: false };
// Эта привязка обязательна для работы `this` в колбэке.
this.handleChecked = this.handleChecked.bind(this);
}
handleChecked(e) {
this.setState((state) => {
return {isChecked: !state.isChecked}
});
}
handleSubmit(event) {
event.preventDefault();
}
render() {
return (
<form onSubmit={this.handleSubmit}>
{ this.props.users.map((user) =>
<FormUserItem key={user.id} name={user.name} isChecked={this.state.isChecked} onChange={this.handleChecked} id={user.id}/>
)}
<div>
<input type="submit" value="Отправить" />
</div>
</form>
);
}
}
const users = [
{ id: 1, name: "User-1" },
{ id: 2, name: "User-2" },
{ id: 3, name: "User-3" }
];
ReactDOM.render(<FormUserItems users={users} />, document.getElementById("root"));
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/16.6.3/umd/react.production.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/16.6.3/umd/react-dom.production.min.js"></script>
<div id="root"></div>
Но состояние при клике меняется сразу у всех чекбоксов.
Как в данной логике поставить состояние конкретному чекбоксу?
Или данный подход совсем не верный?
Если так, то приведите пример верного решения, чтобы не применять заведомо неправильный вариант.
Ответы (2 шт):
Вам нужно что бы у каждого из checkbox'ов был собственный state, тогда при изменении одного из inpit'ов остальные меняться не будут.
Пример на хуках:
const Input = () => {
const [state, setState] = useState(false)
const handleChange = event => {
setState(prevState => !prevState)
}
return (
<input
type="checkbox"
onChange={handleChange}
checked={state}
/>
)
}
const Form = () => {
return (
<div>
<Input />
<Input />
</div>
)
}
Вам может подойти решение пользователя @xydope, где он наглядно показывает как использовать подьем состояния.
Имейте ввиду что если Вы стремитесь к созданию максимально переиспользуемых компонентов то решение с одним стейтом в родителе не являться оптимальным.
Про подъем состояния почитайте.
У вас есть родитель - FormUserItems, который контролирует сhecked дочерних элементов FormUserItem (над названиями поработайте, будете путаться).
В стейте родителя должен храниться стейт с массивом пользователей (в примере users)
Цепочка следующая:
- Родитель создает чилдрена с чекбоксом, в пропсы которого передает ID и забинженую функцию для изменения СВОЕГО(родительского) стейта.
- Чилдрены получают функцию и свой ID, при срабатывании эвента onChange чилдрен вызывает забинженную функцию и передает в нее свой ID из пропсов.
- Забинженая функция, по ID изменяет isChecked юзера, формирует и применяет новый стейт.
- React перерисовывает измененный элемент (важно! не все элементы, а только измененный).
class FormUserItem extends React.Component {
render() {
let className = "user__item";
if (this.props.isChecked) {
className += " active";
}
return (
<label>
<input
className={className}
name={"usr_" + this.props.id}
type="checkbox"
checked={this.props.isChecked}
//handleCheck из пропсов берем
onChange={() => this.props.handleChecked(this.props.id)}
/>
{this.props.name}
</label>
);
}
}
class FormUserItems extends React.Component {
constructor(props) {
super(props);
//юзеров из пропсов мы сразу определяем в стейт и дальше работаем только со стейтом.
this.state = {
users: this.props.users
};
this.handleChecked = this.handleChecked.bind(this);
}
handleChecked(id) {
let users = this.state.users.slice();
let user = users.find(user=>user.id == id);
user.isChecked = !user.isChecked;
this.setState({users});
}
handleSubmit(event) {
event.preventDefault();
}
render() {
return (
<form onSubmit={this.handleSubmit}>
{this.state.users.map((user) => (
<FormUserItem
key={user.id}
name={user.name}
isChecked={user.isChecked || false} //если isChecked нет, то поумолчанию он фолс.
onChange={this.handleChecked}
handleChecked={this.handleChecked}
id={user.id}
/>
))}
<div>
<input type="submit" value="Отправить" />
</div>
</form>
);
}
}
const users = [
{ id: 1, name: "User-1" },
{ id: 2, name: "User-2" },
{ id: 3, name: "User-3" }
];
ReactDOM.render(
<FormUserItems users={users} />,
document.getElementById("root")
);
@import url('https://fonts.googleapis.com/css2?family=Montserrat:wght@100;300;400;500;600;700;900&display=swap');
body{
padding:0;
margin:0;
font-family: 'Montserrat', sans-serif;
}
.user__item{
padding:20px 15px;
background: #0a84ff;
color:#fff;
&.active{
background: #e65e59;}
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/16.6.3/umd/react.production.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/16.6.3/umd/react-dom.production.min.js"></script>
<div id="root"></div>