Как работать с чекбоксами в React

Возможно, вы не раз сталкивались с ситуациями, когда вам приходилось использовать чекбоксы, например, при согласии с условиями, выборе списка предпочтений и т.д. В этой статье мы рассмотрим различные сценарии использования чекбоксов в 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
Вход в полноэкранный режим Выход из полноэкранного режима

Оцените статью
devanswers.ru
Добавить комментарий