React 教程——如何使用多个复选框

在 React 中处理多个复选框与使用常规 HTML 复选框的方式完全不同。

所以在本文中,我们将看到如何在 React 中使用多个复选框。

你将学习:

  • 如何在 React 中使用复选框作为受控输入
  • 如何使用数组 map 和 reduce 方法进行复杂计算
  • 如何创建预先填充了某些特定值的特定长度的数组

以及更多。

本文是我的 Mastering Redux 课程的一部分。这是我们将在课程中构建的应用程序的预览。

让我们开始吧。

如何使用单个复选框

让我们从单个复选框功能开始,然后再转到多个复选框。

在本文中,我将使用 React Hooks 语法来创建组件。因此,如果你不熟悉 React Hooks,请查看我的 React Hooks 简介文章。

看看下面的代码:

<div className="App">
  Select your pizza topping:
  <div className="topping">
    <input type="checkbox" id="topping" name="topping" value="Paneer" />Paneer
  </div>
</div>

这是一个代码示例。

在上面的代码中,我们只声明了一个复选框,类似于我们声明 HTML 复选框的方式。

因此,我们可以轻松地选中和取消选中复选框,如下所示:

React 教程——如何使用多个复选框

但是要在屏幕上显示它是否被选中,我们需要将其转换为受控输入。

在 React 中,受控输入由状态管理,因此只能通过更改与该输入相关的状态来更改输入值。

看看下面的代码:

export default function App() {
  const [isChecked, setIsChecked] = useState(false);

  const handleOnChange = () => {
    setIsChecked(!isChecked);
  };

  return (
    <div className="App">
      Select your pizza topping:
      <div className="topping">
        <input
          type="checkbox"
          id="topping"
          name="topping"
          value="Paneer"
          checked={isChecked}
          onChange={handleOnChange}
        />
        Paneer
      </div>
      <div className="result">
        Above checkbox is {isChecked ? "checked" : "un-checked"}.
      </div>
    </div>
  );
}

这是一个代码示例。

在上面的代码中,我们使用 useState 钩子在组件中声明了 isChecked 状态,初始值为 false

const [isChecked, setIsChecked] = useState(false);

然后对于输入复选框,我们添加两个 prop,即 checked 和 onChange

<input
  ...
  checked={isChecked}
  onChange={handleOnChange}
/>

每当我们单击复选框时,将调用 handleOnChange 函数,我们使用它来设置 isChecked 状态的值。

const handleOnChange = () => {
  setIsChecked(!isChecked);
};

因此,如果复选框被选中,我们将 isChecked 值设置为 false。但是如果未选中该复选框,我们将使用 !isChecked 将值设置为 true。然后我们将该值传递给 prop checked 的输入复选框。

这样,输入复选框成为受控输入,其值由状态管理。

请注意,在 React 中,即使代码看起来很复杂,也始终建议对输入字段使用受控输入。这保证了输入更改仅发生在 onChange 程序内部。

输入的状态不会以任何其他方式更改,你将始终获得输入状态的正确的和更新的值。

只有在极少数情况下,你可以使用 React ref 以不受控制的方式使用输入。

如何处理多个复选框

现在,让我们看看如何处理多个复选框。

看一下这个代码示例。

multiple_checkboxes-2

在这里,我们显示了配料列表及其相应的价格。根据选择的配料,我们需要显示总量。

以前,对于单个复选框,我们只有 isChecked 状态,我们根据它更改了复选框的状态。

但是现在我们有很多复选框,因此为每个复选框添加多个 useState 调用是不切实际的。

因此,让我们在 state 中声明一个数组,指示每个复选框的状态。

要创建一个等于复选框数量长度的数组,我们可以使用数组 fill 方法,如下所示:

const [checkedState, setCheckedState] = useState(
    new Array(toppings.length).fill(false)
);

在这里,我们将具有初始值的状态声明为填充值 false 的数组。

因此,如果我们有 5 个配料,那么 checkedState 状态数组将包含 5 个 false 值,如下所示:

[false, false, false, false, false]

一旦我们选中/取消选中复选框,我们会将相应的 false 更改为 true,并将 true 更改为 false

这是最终的代码示例。

完整的 App.js 代码如下所示:

import { useState } from "react";
import { toppings } from "./utils/toppings";
import "./styles.css";

const getFormattedPrice = (price) => `$${price.toFixed(2)}`;

export default function App() {
  const [checkedState, setCheckedState] = useState(
    new Array(toppings.length).fill(false)
  );

  const [total, setTotal] = useState(0);

  const handleOnChange = (position) => {
    const updatedCheckedState = checkedState.map((item, index) =>
      index === position ? !item : item
    );

    setCheckedState(updatedCheckedState);

    const totalPrice = updatedCheckedState.reduce(
      (sum, currentState, index) => {
        if (currentState === true) {
          return sum + toppings[index].price;
        }
        return sum;
      },
      0
    );

    setTotal(totalPrice);
  };

  return (
    <div className="App">
      <h3>Select Toppings</h3>
      <ul className="toppings-list">
        {toppings.map(({ name, price }, index) => {
          return (
            <li key={index}>
              <div className="toppings-list-item">
                <div className="left-section">
                  <input
                    type="checkbox"
                    id={`custom-checkbox-${index}`}
                    name={name}
                    value={name}
                    checked={checkedState[index]}
                    onChange={() => handleOnChange(index)}
                  />
                  <label htmlFor={`custom-checkbox-${index}`}>{name}</label>
                </div>
                <div className="right-section">{getFormattedPrice(price)}</div>
              </div>
            </li>
          );
        })}
        <li>
          <div className="toppings-list-item">
            <div className="left-section">Total:</div>
            <div className="right-section">{getFormattedPrice(total)}</div>
          </div>
        </li>
      </ul>
    </div>
  );
}

让我们理解我们在这里做什么。

我们声明了输入复选框,如下所示:

<input
  type="checkbox"
  id={`custom-checkbox-${index}`}
  name={name}
  value={name}
  checked={checkedState[index]}
  onChange={() => handleOnChange(index)}
/>

在这里,我们从 checkedState 状态添加了一个 checked 属性,对应的值为 true 或 false。因此,每个复选框都将具有其选中状态的正确值。

我们还添加了一个 onChange 程序,并将选中/取消选中的复选框的索引 index 传递给 handleOnChange 方法。

handleOnChange 程序方法如下所示:

const handleOnChange = (position) => {
  const updatedCheckedState = checkedState.map((item, index) =>
    index === position ? !item : item
  );

  setCheckedState(updatedCheckedState);

  const totalPrice = updatedCheckedState.reduce(
    (sum, currentState, index) => {
      if (currentState === true) {
        return sum + toppings[index].price;
      }
      return sum;
    },
    0
  );

  setTotal(totalPrice);
};

在这里,我们首先使用数组 map 方法遍历 checkedState 数组。如果传递的 position 参数的值与当前 index 匹配,那么我们反转它的值。然后,如果值为 true,则使用 !item 将其转换为 false,如果值为 false,则将其转换为 true

如果 index 与提供的 position 参数不匹配,那么我们不会反转它的值,而只是按原样返回值。

const updatedCheckedState = checkedState.map((item, index) =>
  index === position ? !item : item
);

// 上面的代码与下面的代码相同

const updatedCheckedState = checkedState.map((item, index) => {
  if (index === position) {
    return !item;
  } else {
    return item;
  }
});

我使用了三元运算符 ?:,因为它使代码更短,但你可以使用任何数组方法。

如果你不熟悉 map 或 reduce 等数组方法的工作原理,请查看我写的这篇文章。

接下来,我们将 checkedState 数组设置为 updatedCheckedState 数组。这很重要,因为如果你不更新 handleOnChange 程序中的 checkedState 状态,那么你将无法选中/取消选中该复选框。

这是因为我们使用复选框的 checkedState 值来确定复选框是否被选中(因为它是一个受控输入,如下所示):

<input
  type="checkbox"
  ...
  checked={checkedState[index]}
  onChange={() => handleOnChange(index)}
/>

请注意,我们创建了一个单独的 updatedCheckedState 变量,并将该变量传递给 setCheckedState 函数。我们在 updatedCheckedState 上使用 reduce 方法,而不是在原始的 checkState 数组上。

这是因为默认情况下,用于更新状态的 setCheckedState 函数是异步的。

仅仅因为你调用了 setCheckedState 函数并不能保证你将在下一行中获得 checkedState 数组的更新值。

所以我们创建了一个单独的变量并在 reduce 方法中使用它。

如果你不熟悉 React 中状态的工作原理,可以阅读这篇文章。

然后为了计算总价,我们使用数组 reduce 方法:

const totalPrice = updatedCheckedState.reduce(
  (sum, currentState, index) => {
    if (currentState === true) {
      return sum + toppings[index].price;
    }
    return sum;
  },
  0
);

数组 reduce 方法接收四个参数,其中我们只使用了三个:sumcurrentState 和 index。如果需要,你可以使用不同的名称,因为它们只是参数。

我们还将 0 作为初始值传递,也称为 sum 参数的 accumulator 值。

然后在 reduce 函数内部,我们检查 checkedState 数组的当前值是否为 true

如果为 true,则表示复选框已选中,因此我们使用 sum + toppings[index].price 添加相应 price 的值。

如果 checkedState 数组的值为 false,那么我们不会添加它的价格,而只是返回计算出的之前的 sum 值。

然后我们使用 setTotal(totalPrice) 将该 totalPrice 值设置为 total 状态。

通过这种方式,我们能够正确计算所选配料的总价格,如下所示:

toppings-1

这是上述代码的预览链接,你可以自己尝试一下。

在 React 中处理多个复选框与使用常规 HTML 复选框的方式完全不同。

所以在本文中,我们将看到如何在 React 中使用多个复选框。

你将学习:

  • 如何在 React 中使用复选框作为受控输入
  • 如何使用数组 map 和 reduce 方法进行复杂计算
  • 如何创建预先填充了某些特定值的特定长度的数组

以及更多。

本文是我的 Mastering Redux 课程的一部分。这是我们将在课程中构建的应用程序的预览。

让我们开始吧。

如何使用单个复选框

让我们从单个复选框功能开始,然后再转到多个复选框。

在本文中,我将使用 React Hooks 语法来创建组件。因此,如果你不熟悉 React Hooks,请查看我的 React Hooks 简介文章。

看看下面的代码:

<div className="App">
  Select your pizza topping:
  <div className="topping">
    <input type="checkbox" id="topping" name="topping" value="Paneer" />Paneer
  </div>
</div>

这是一个代码示例。

在上面的代码中,我们只声明了一个复选框,类似于我们声明 HTML 复选框的方式。

因此,我们可以轻松地选中和取消选中复选框,如下所示:

React 教程——如何使用多个复选框

但是要在屏幕上显示它是否被选中,我们需要将其转换为受控输入。

在 React 中,受控输入由状态管理,因此只能通过更改与该输入相关的状态来更改输入值。

看看下面的代码:

export default function App() {
  const [isChecked, setIsChecked] = useState(false);

  const handleOnChange = () => {
    setIsChecked(!isChecked);
  };

  return (
    <div className="App">
      Select your pizza topping:
      <div className="topping">
        <input
          type="checkbox"
          id="topping"
          name="topping"
          value="Paneer"
          checked={isChecked}
          onChange={handleOnChange}
        />
        Paneer
      </div>
      <div className="result">
        Above checkbox is {isChecked ? "checked" : "un-checked"}.
      </div>
    </div>
  );
}

这是一个代码示例。

在上面的代码中,我们使用 useState 钩子在组件中声明了 isChecked 状态,初始值为 false

const [isChecked, setIsChecked] = useState(false);

然后对于输入复选框,我们添加两个 prop,即 checked 和 onChange

<input
  ...
  checked={isChecked}
  onChange={handleOnChange}
/>

每当我们单击复选框时,将调用 handleOnChange 函数,我们使用它来设置 isChecked 状态的值。

const handleOnChange = () => {
  setIsChecked(!isChecked);
};

因此,如果复选框被选中,我们将 isChecked 值设置为 false。但是如果未选中该复选框,我们将使用 !isChecked 将值设置为 true。然后我们将该值传递给 prop checked 的输入复选框。

这样,输入复选框成为受控输入,其值由状态管理。

请注意,在 React 中,即使代码看起来很复杂,也始终建议对输入字段使用受控输入。这保证了输入更改仅发生在 onChange 程序内部。

输入的状态不会以任何其他方式更改,你将始终获得输入状态的正确的和更新的值。

只有在极少数情况下,你可以使用 React ref 以不受控制的方式使用输入。

如何处理多个复选框

现在,让我们看看如何处理多个复选框。

看一下这个代码示例。

React 教程——如何使用多个复选框

在这里,我们显示了配料列表及其相应的价格。根据选择的配料,我们需要显示总量。

以前,对于单个复选框,我们只有 isChecked 状态,我们根据它更改了复选框的状态。

但是现在我们有很多复选框,因此为每个复选框添加多个 useState 调用是不切实际的。

因此,让我们在 state 中声明一个数组,指示每个复选框的状态。

要创建一个等于复选框数量长度的数组,我们可以使用数组 fill 方法,如下所示:

const [checkedState, setCheckedState] = useState(
    new Array(toppings.length).fill(false)
);

在这里,我们将具有初始值的状态声明为填充值 false 的数组。

因此,如果我们有 5 个配料,那么 checkedState 状态数组将包含 5 个 false 值,如下所示:

[false, false, false, false, false]

一旦我们选中/取消选中复选框,我们会将相应的 false 更改为 true,并将 true 更改为 false

这是最终的代码示例。

完整的 App.js 代码如下所示:

import { useState } from "react";
import { toppings } from "./utils/toppings";
import "./styles.css";

const getFormattedPrice = (price) => `$${price.toFixed(2)}`;

export default function App() {
  const [checkedState, setCheckedState] = useState(
    new Array(toppings.length).fill(false)
  );

  const [total, setTotal] = useState(0);

  const handleOnChange = (position) => {
    const updatedCheckedState = checkedState.map((item, index) =>
      index === position ? !item : item
    );

    setCheckedState(updatedCheckedState);

    const totalPrice = updatedCheckedState.reduce(
      (sum, currentState, index) => {
        if (currentState === true) {
          return sum + toppings[index].price;
        }
        return sum;
      },
      0
    );

    setTotal(totalPrice);
  };

  return (
    <div className="App">
      <h3>Select Toppings</h3>
      <ul className="toppings-list">
        {toppings.map(({ name, price }, index) => {
          return (
            <li key={index}>
              <div className="toppings-list-item">
                <div className="left-section">
                  <input
                    type="checkbox"
                    id={`custom-checkbox-${index}`}
                    name={name}
                    value={name}
                    checked={checkedState[index]}
                    onChange={() => handleOnChange(index)}
                  />
                  <label htmlFor={`custom-checkbox-${index}`}>{name}</label>
                </div>
                <div className="right-section">{getFormattedPrice(price)}</div>
              </div>
            </li>
          );
        })}
        <li>
          <div className="toppings-list-item">
            <div className="left-section">Total:</div>
            <div className="right-section">{getFormattedPrice(total)}</div>
          </div>
        </li>
      </ul>
    </div>
  );
}

让我们理解我们在这里做什么。

我们声明了输入复选框,如下所示:

<input
  type="checkbox"
  id={`custom-checkbox-${index}`}
  name={name}
  value={name}
  checked={checkedState[index]}
  onChange={() => handleOnChange(index)}
/>

在这里,我们从 checkedState 状态添加了一个 checked 属性,对应的值为 true 或 false。因此,每个复选框都将具有其选中状态的正确值。

我们还添加了一个 onChange 程序,并将选中/取消选中的复选框的索引 index 传递给 handleOnChange 方法。

handleOnChange 程序方法如下所示:

const handleOnChange = (position) => {
  const updatedCheckedState = checkedState.map((item, index) =>
    index === position ? !item : item
  );

  setCheckedState(updatedCheckedState);

  const totalPrice = updatedCheckedState.reduce(
    (sum, currentState, index) => {
      if (currentState === true) {
        return sum + toppings[index].price;
      }
      return sum;
    },
    0
  );

  setTotal(totalPrice);
};

在这里,我们首先使用数组 map 方法遍历 checkedState 数组。如果传递的 position 参数的值与当前 index 匹配,那么我们反转它的值。然后,如果值为 true,则使用 !item 将其转换为 false,如果值为 false,则将其转换为 true

如果 index 与提供的 position 参数不匹配,那么我们不会反转它的值,而只是按原样返回值。

const updatedCheckedState = checkedState.map((item, index) =>
  index === position ? !item : item
);

// 上面的代码与下面的代码相同

const updatedCheckedState = checkedState.map((item, index) => {
  if (index === position) {
    return !item;
  } else {
    return item;
  }
});

我使用了三元运算符 ?:,因为它使代码更短,但你可以使用任何数组方法。

如果你不熟悉 map 或 reduce 等数组方法的工作原理,请查看我写的这篇文章。

接下来,我们将 checkedState 数组设置为 updatedCheckedState 数组。这很重要,因为如果你不更新 handleOnChange 程序中的 checkedState 状态,那么你将无法选中/取消选中该复选框。

这是因为我们使用复选框的 checkedState 值来确定复选框是否被选中(因为它是一个受控输入,如下所示):

<input
  type="checkbox"
  ...
  checked={checkedState[index]}
  onChange={() => handleOnChange(index)}
/>

请注意,我们创建了一个单独的 updatedCheckedState 变量,并将该变量传递给 setCheckedState 函数。我们在 updatedCheckedState 上使用 reduce 方法,而不是在原始的 checkState 数组上。

这是因为默认情况下,用于更新状态的 setCheckedState 函数是异步的。

仅仅因为你调用了 setCheckedState 函数并不能保证你将在下一行中获得 checkedState 数组的更新值。

所以我们创建了一个单独的变量并在 reduce 方法中使用它。

如果你不熟悉 React 中状态的工作原理,可以阅读这篇文章。

然后为了计算总价,我们使用数组 reduce 方法:

const totalPrice = updatedCheckedState.reduce(
  (sum, currentState, index) => {
    if (currentState === true) {
      return sum + toppings[index].price;
    }
    return sum;
  },
  0
);

数组 reduce 方法接收四个参数,其中我们只使用了三个:sumcurrentState 和 index。如果需要,你可以使用不同的名称,因为它们只是参数。

我们还将 0 作为初始值传递,也称为 sum 参数的 accumulator 值。

然后在 reduce 函数内部,我们检查 checkedState 数组的当前值是否为 true

如果为 true,则表示复选框已选中,因此我们使用 sum + toppings[index].price 添加相应 price 的值。

如果 checkedState 数组的值为 false,那么我们不会添加它的价格,而只是返回计算出的之前的 sum 值。

然后我们使用 setTotal(totalPrice) 将该 totalPrice 值设置为 total 状态。

通过这种方式,我们能够正确计算所选配料的总价格,如下所示:

toppings-1

这是上述代码的预览链接,你可以自己尝试一下。

在 React 中处理多个复选框与使用常规 HTML 复选框的方式完全不同。

所以在本文中,我们将看到如何在 React 中使用多个复选框。

你将学习:

  • 如何在 React 中使用复选框作为受控输入
  • 如何使用数组 map 和 reduce 方法进行复杂计算
  • 如何创建预先填充了某些特定值的特定长度的数组

以及更多。

本文是我的 Mastering Redux 课程的一部分。这是我们将在课程中构建的应用程序的预览。

让我们开始吧。

如何使用单个复选框

让我们从单个复选框功能开始,然后再转到多个复选框。

在本文中,我将使用 React Hooks 语法来创建组件。因此,如果你不熟悉 React Hooks,请查看我的 React Hooks 简介文章。

看看下面的代码:

<div className="App">
  Select your pizza topping:
  <div className="topping">
    <input type="checkbox" id="topping" name="topping" value="Paneer" />Paneer
  </div>
</div>

这是一个代码示例。

在上面的代码中,我们只声明了一个复选框,类似于我们声明 HTML 复选框的方式。

因此,我们可以轻松地选中和取消选中复选框,如下所示:

React 教程——如何使用多个复选框

但是要在屏幕上显示它是否被选中,我们需要将其转换为受控输入。

在 React 中,受控输入由状态管理,因此只能通过更改与该输入相关的状态来更改输入值。

看看下面的代码:

export default function App() {
  const [isChecked, setIsChecked] = useState(false);

  const handleOnChange = () => {
    setIsChecked(!isChecked);
  };

  return (
    <div className="App">
      Select your pizza topping:
      <div className="topping">
        <input
          type="checkbox"
          id="topping"
          name="topping"
          value="Paneer"
          checked={isChecked}
          onChange={handleOnChange}
        />
        Paneer
      </div>
      <div className="result">
        Above checkbox is {isChecked ? "checked" : "un-checked"}.
      </div>
    </div>
  );
}

这是一个代码示例。

在上面的代码中,我们使用 useState 钩子在组件中声明了 isChecked 状态,初始值为 false

const [isChecked, setIsChecked] = useState(false);

然后对于输入复选框,我们添加两个 prop,即 checked 和 onChange

<input
  ...
  checked={isChecked}
  onChange={handleOnChange}
/>

每当我们单击复选框时,将调用 handleOnChange 函数,我们使用它来设置 isChecked 状态的值。

const handleOnChange = () => {
  setIsChecked(!isChecked);
};

因此,如果复选框被选中,我们将 isChecked 值设置为 false。但是如果未选中该复选框,我们将使用 !isChecked 将值设置为 true。然后我们将该值传递给 prop checked 的输入复选框。

这样,输入复选框成为受控输入,其值由状态管理。

请注意,在 React 中,即使代码看起来很复杂,也始终建议对输入字段使用受控输入。这保证了输入更改仅发生在 onChange 程序内部。

输入的状态不会以任何其他方式更改,你将始终获得输入状态的正确的和更新的值。

只有在极少数情况下,你可以使用 React ref 以不受控制的方式使用输入。

如何处理多个复选框

现在,让我们看看如何处理多个复选框。

看一下这个代码示例。

React 教程——如何使用多个复选框

在这里,我们显示了配料列表及其相应的价格。根据选择的配料,我们需要显示总量。

以前,对于单个复选框,我们只有 isChecked 状态,我们根据它更改了复选框的状态。

但是现在我们有很多复选框,因此为每个复选框添加多个 useState 调用是不切实际的。

因此,让我们在 state 中声明一个数组,指示每个复选框的状态。

要创建一个等于复选框数量长度的数组,我们可以使用数组 fill 方法,如下所示:

const [checkedState, setCheckedState] = useState(
    new Array(toppings.length).fill(false)
);

在这里,我们将具有初始值的状态声明为填充值 false 的数组。

因此,如果我们有 5 个配料,那么 checkedState 状态数组将包含 5 个 false 值,如下所示:

[false, false, false, false, false]

一旦我们选中/取消选中复选框,我们会将相应的 false 更改为 true,并将 true 更改为 false

这是最终的代码示例。

完整的 App.js 代码如下所示:

import { useState } from "react";
import { toppings } from "./utils/toppings";
import "./styles.css";

const getFormattedPrice = (price) => `$${price.toFixed(2)}`;

export default function App() {
  const [checkedState, setCheckedState] = useState(
    new Array(toppings.length).fill(false)
  );

  const [total, setTotal] = useState(0);

  const handleOnChange = (position) => {
    const updatedCheckedState = checkedState.map((item, index) =>
      index === position ? !item : item
    );

    setCheckedState(updatedCheckedState);

    const totalPrice = updatedCheckedState.reduce(
      (sum, currentState, index) => {
        if (currentState === true) {
          return sum + toppings[index].price;
        }
        return sum;
      },
      0
    );

    setTotal(totalPrice);
  };

  return (
    <div className="App">
      <h3>Select Toppings</h3>
      <ul className="toppings-list">
        {toppings.map(({ name, price }, index) => {
          return (
            <li key={index}>
              <div className="toppings-list-item">
                <div className="left-section">
                  <input
                    type="checkbox"
                    id={`custom-checkbox-${index}`}
                    name={name}
                    value={name}
                    checked={checkedState[index]}
                    onChange={() => handleOnChange(index)}
                  />
                  <label htmlFor={`custom-checkbox-${index}`}>{name}</label>
                </div>
                <div className="right-section">{getFormattedPrice(price)}</div>
              </div>
            </li>
          );
        })}
        <li>
          <div className="toppings-list-item">
            <div className="left-section">Total:</div>
            <div className="right-section">{getFormattedPrice(total)}</div>
          </div>
        </li>
      </ul>
    </div>
  );
}

让我们理解我们在这里做什么。

我们声明了输入复选框,如下所示:

<input
  type="checkbox"
  id={`custom-checkbox-${index}`}
  name={name}
  value={name}
  checked={checkedState[index]}
  onChange={() => handleOnChange(index)}
/>

在这里,我们从 checkedState 状态添加了一个 checked 属性,对应的值为 true 或 false。因此,每个复选框都将具有其选中状态的正确值。

我们还添加了一个 onChange 程序,并将选中/取消选中的复选框的索引 index 传递给 handleOnChange 方法。

handleOnChange 程序方法如下所示:

const handleOnChange = (position) => {
  const updatedCheckedState = checkedState.map((item, index) =>
    index === position ? !item : item
  );

  setCheckedState(updatedCheckedState);

  const totalPrice = updatedCheckedState.reduce(
    (sum, currentState, index) => {
      if (currentState === true) {
        return sum + toppings[index].price;
      }
      return sum;
    },
    0
  );

  setTotal(totalPrice);
};

在这里,我们首先使用数组 map 方法遍历 checkedState 数组。如果传递的 position 参数的值与当前 index 匹配,那么我们反转它的值。然后,如果值为 true,则使用 !item 将其转换为 false,如果值为 false,则将其转换为 true

如果 index 与提供的 position 参数不匹配,那么我们不会反转它的值,而只是按原样返回值。

const updatedCheckedState = checkedState.map((item, index) =>
  index === position ? !item : item
);

// 上面的代码与下面的代码相同

const updatedCheckedState = checkedState.map((item, index) => {
  if (index === position) {
    return !item;
  } else {
    return item;
  }
});

我使用了三元运算符 ?:,因为它使代码更短,但你可以使用任何数组方法。

如果你不熟悉 map 或 reduce 等数组方法的工作原理,请查看我写的这篇文章。

接下来,我们将 checkedState 数组设置为 updatedCheckedState 数组。这很重要,因为如果你不更新 handleOnChange 程序中的 checkedState 状态,那么你将无法选中/取消选中该复选框。

这是因为我们使用复选框的 checkedState 值来确定复选框是否被选中(因为它是一个受控输入,如下所示):

<input
  type="checkbox"
  ...
  checked={checkedState[index]}
  onChange={() => handleOnChange(index)}
/>

请注意,我们创建了一个单独的 updatedCheckedState 变量,并将该变量传递给 setCheckedState 函数。我们在 updatedCheckedState 上使用 reduce 方法,而不是在原始的 checkState 数组上。

这是因为默认情况下,用于更新状态的 setCheckedState 函数是异步的。

仅仅因为你调用了 setCheckedState 函数并不能保证你将在下一行中获得 checkedState 数组的更新值。

所以我们创建了一个单独的变量并在 reduce 方法中使用它。

如果你不熟悉 React 中状态的工作原理,可以阅读这篇文章。

然后为了计算总价,我们使用数组 reduce 方法:

const totalPrice = updatedCheckedState.reduce(
  (sum, currentState, index) => {
    if (currentState === true) {
      return sum + toppings[index].price;
    }
    return sum;
  },
  0
);

数组 reduce 方法接收四个参数,其中我们只使用了三个:sumcurrentState 和 index。如果需要,你可以使用不同的名称,因为它们只是参数。

我们还将 0 作为初始值传递,也称为 sum 参数的 accumulator 值。

然后在 reduce 函数内部,我们检查 checkedState 数组的当前值是否为 true

如果为 true,则表示复选框已选中,因此我们使用 sum + toppings[index].price 添加相应 price 的值。

如果 checkedState 数组的值为 false,那么我们不会添加它的价格,而只是返回计算出的之前的 sum 值。

然后我们使用 setTotal(totalPrice) 将该 totalPrice 值设置为 total 状态。

通过这种方式,我们能够正确计算所选配料的总价格,如下所示:

React 教程——如何使用多个复选框

这是上述代码的预览链接,你可以自己尝试一下。