Возможно, вы не раз сталкивались с ситуациями, когда вам приходилось использовать чекбоксы, например, при согласии с условиями, выборе списка предпочтений и т.д. В этой статье мы рассмотрим различные сценарии использования чекбоксов в React.
Сначала создадим простой компонент флажка, как показано ниже:
export const Checkbox = () => {
return (
<div>
<input type="checkbox" id="checkbox" />
<label htmlFor="checkbox">I agree to Terms of Service </label>
</div>
)
}
function App() {
return (
<div className="App">
<Checkbox />
</div>
)
}
export default App
Теперь, если вы протестируете приложение, вы увидите, что флажок можно установить и снять. Но как узнать текущее состояние флажка?
Хранение и чтение состояния флажка
Мы можем использовать хук useState для хранения состояния флажка.
import { useState } from "react"
export const Checkbox = () => {
const [isChecked, setIsChecked] = useState(false)
return (
<div>
<input type="checkbox" id="checkbox" checked={isChecked} />
<label htmlFor="checkbox">I agree to Terms of Service </label>
<p>The checkbox is {isChecked ? "checked" : "unchecked"}</p>
</div>
)
}
function App() {
return (
<div className="App">
<Checkbox />
</div>
)
}
export default App
Теперь, если вы попытаетесь установить флажок, ничего не произойдет, а в консоли вы увидите следующее предупреждение:
You provided a checked prop to a form field without an onChange handler. This will render a read-only field. If the field should be mutable use defaultChecked. Otherwise, set either onChange or readOnly.
Почему это происходит? Как следует из предупреждения, мы просто устанавливаем значение состояния для флажка и ничего не делаем, пока состояние флажка меняется. Поэтому давайте привяжем обработчик изменения состояния:
import { useState } from "react"
export const Checkbox = () => {
const [isChecked, setIsChecked] = useState(false)
const checkHandler = () => {
setIsChecked(!isChecked)
}
return (
<div>
<input
type="checkbox"
id="checkbox"
checked={isChecked}
onChange={checkHandler}
/>
<label htmlFor="checkbox">I agree to Terms of Service </label>
<p>The checkbox is {isChecked ? "checked" : "unchecked"}</p>
</div>
)
}
function App() {
return (
<div className="App">
<Checkbox />
</div>
)
}
export default App
Если вы хотите, чтобы флажок был установлен изначально, то вы можете передать true
в хук useState при его инициализации.
Флажок с использованием неконтролируемого ввода
В рассмотренном выше примере мы использовали управляемые входы. Далее мы рассмотрим, как реализовать то же самое, используя неконтролируемые входы.
Я уже объяснял разницу между управляемыми и неуправляемыми входами в своей предыдущей статье.
import { useRef } from "react"
function App() {
const checkboxRef = useRef(null)
const formSubmitHandler = e => {
e.preventDefault()
alert(
`The checkbox is ${checkboxRef.current.checked ? "checked" : "unchecked"}`
)
}
return (
<div className="App">
<form onSubmit={formSubmitHandler}>
<input
type="checkbox"
id="checkbox"
defaultChecked={true}
ref={checkboxRef}
/>
<label htmlFor="checkbox">I agree to Terms of Service </label>
<p>
<button type="submit">Submit</button>
</p>
</form>
</div>
)
}
export default App
Здесь мы предоставляем начальное значение с помощью свойства defaultChecked
. Мы создали ссылку на флажок, чтобы мы могли получить доступ к его значению внутри обработчика отправки формы.
Здесь мы не используем никакого состояния для хранения текущего состояния флажка. Оно хранится в самом DOM.
Всегда рекомендуется использовать управляемые компоненты вместо неуправляемых, поскольку, как следует из названия, мы имеем больше контроля над вводом.
Повторное использование компонента checkbox для отображения нескольких флажков
Во-первых, давайте сделаем компонент checkbox, который мы создали ранее, компонентом многократного использования:
«jsx App.js
import { useState } from «react»
export const Checkbox = ({ isChecked, label, checkHandler }) => {
return (
type=»checkbox»
id=»checkbox»
checked={isChecked}
onChange={checkHandler}
/>
{label}
)
}
function App() {
const [isChecked, setIsChecked] = useState(false)
const checkHandler = () => {
setIsChecked(!isChecked)
}
return (
isChecked={isChecked}
checkHandler={checkHandler}
label=»Я согласен с условиями предоставления услуг»
/>
Чекбокс имеет значение {isChecked ? «checked» : «unchecked»}
)
}
export default App
Now let's say you have a use case where you want to display a list of pizza toppings, from which you want the users to choose. We can achieve that by the following code:
```jsx
import { useState } from "react"
const allToppings = [
{ name: "Golden Corn", checked: false },
{ name: "Paneer", checked: false },
{ name: "Tomato", checked: false },
{ name: "Mushroom", checked: false },
{ name: "Onion", checked: false },
{ name: "Black Olives", checked: false },
]
export const Checkbox = ({ isChecked, label, checkHandler, index }) => {
return (
<div>
<input
type="checkbox"
id={`checkbox-${index}`}
checked={isChecked}
onChange={checkHandler}
/>
<label htmlFor={`checkbox-${index}`}>{label}</label>
</div>
)
}
function App() {
const [toppings, setToppings] = useState(allToppings)
const updateCheckStatus = index => {
setToppings(
toppings.map((topping, currentIndex) =>
currentIndex === index
? { ...topping, checked: !topping.checked }
: topping
)
)
// or
// setToppings([
// ...toppings.slice(0, index),
// { ...toppings[index], checked: !toppings[index].checked },
// ...toppings.slice(index + 1),
// ]);
}
return (
<div className="App">
{toppings.map((topping, index) => (
<Checkbox
key={topping.name}
isChecked={topping.checked}
checkHandler={() => updateCheckStatus(index)}
label={topping.name}
index={index}
/>
))}
<p>
<pre>{JSON.stringify(toppings, null, 2)}</pre>
</p>
</div>
)
}
export default App
Здесь мы храним статус проверки флажка в локальном состоянии toppings
. Мы написали метод updateCheckStatus
, который будет вызван с индексом измененного флажка и обновит локальное состояние. Мы также отображаем текущее состояние в формате JSON, чтобы мы могли убедиться, что все работает так, как ожидалось.
Выбрать все и снять выделение
Мы можем реализовать функции select all и unselect all, просто обновив состояние всех флажков на true и false соответственно.
import { useState } from "react"
const allToppings = [
{ name: "Golden Corn", checked: false },
{ name: "Paneer", checked: false },
{ name: "Tomato", checked: false },
{ name: "Mushroom", checked: false },
{ name: "Onion", checked: false },
{ name: "Black Olives", checked: false },
]
export const Checkbox = ({ isChecked, label, checkHandler, index }) => {
console.log({ isChecked })
return (
<div>
<input
type="checkbox"
id={`checkbox-${index}`}
checked={isChecked}
onChange={checkHandler}
/>
<label htmlFor={`checkbox-${index}`}>{label}</label>
</div>
)
}
function App() {
const [toppings, setToppings] = useState(allToppings)
const updateCheckStatus = index => {
setToppings(
toppings.map((topping, currentIndex) =>
currentIndex === index
? { ...topping, checked: !topping.checked }
: topping
)
)
// or
// setToppings([
// ...toppings.slice(0, index),
// { ...toppings[index], checked: !toppings[index].checked },
// ...toppings.slice(index + 1),
// ]);
}
const selectAll = () => {
setToppings(toppings.map(topping => ({ ...topping, checked: true })))
}
const unSelectAll = () => {
setToppings(toppings.map(topping => ({ ...topping, checked: false })))
}
return (
<div className="App">
<p>
<button onClick={selectAll}>Select All</button>
<button onClick={unSelectAll}>Unselect All</button>
</p>
{toppings.map((topping, index) => (
<Checkbox
key={topping.name}
isChecked={topping.checked}
checkHandler={() => updateCheckStatus(index)}
label={topping.name}
index={index}
/>
))}
<p>
<pre>{JSON.stringify(toppings, null, 2)}</pre>
</p>
</div>
)
}
export default App