原文: https://www.freecodecamp.org/news/how-to-work-with-multiple-checkboxes-in-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 复选框的方式。
因此,我们可以轻松地选中和取消选中复选框,如下所示:
![check_uncheck-1](https://www.freecodecamp.org/news/content/images/2021/05/check_uncheck-1.gif)
但是要在屏幕上显示它是否被选中,我们需要将其转换为受控输入。
在 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](https://www.freecodecamp.org/news/content/images/2021/05/multiple_checkboxes-2.png)
在这里,我们显示了配料列表及其相应的价格。根据选择的配料,我们需要显示总量。
以前,对于单个复选框,我们只有 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
方法接收四个参数,其中我们只使用了三个:sum
、currentState
和 index
。如果需要,你可以使用不同的名称,因为它们只是参数。
我们还将 0
作为初始值传递,也称为 sum
参数的 accumulator
值。
然后在 reduce
函数内部,我们检查 checkedState
数组的当前值是否为 true
。
如果为 true
,则表示复选框已选中,因此我们使用 sum + toppings[index].price
添加相应 price
的值。
如果 checkedState
数组的值为 false
,那么我们不会添加它的价格,而只是返回计算出的之前的 sum
值。
然后我们使用 setTotal(totalPrice)
将该 totalPrice
值设置为 total
状态。
通过这种方式,我们能够正确计算所选配料的总价格,如下所示:
![toppings-1](https://www.freecodecamp.org/news/content/images/2021/05/toppings-1.gif)
这是上述代码的预览链接,你可以自己尝试一下。
谢谢阅读
大多数开发人员都难以理解 Redux 的工作原理。但是每个 React 开发人员都应该知道如何使用 Redux,因为行业项目大多使用 Redux 来管理大型项目。
因此,为了方便你学习,我推出了 Mastering Redux 课程。
在本课程中,你将从零开始学习 Redux,你还将使用 Redux 从零开始构建一个完整的食品订购应用程序。
单击下图加入课程并获得限时折扣优惠,并免费获得我的 Mastering Modern JavaScript 书籍。
![banner](https://gist.github.com/myogeshchavan97/98ae4f4ead57fde8d47fcf7641220b72/raw/c3e4265df4396d639a7938a83bffd570130483b1/banner.jpg)
想要及时了解有关 JavaScript、React、Node.js 的内容吗?在 LinkedIn 上关注我。