先记住这个答案
需要 React 随业务状态控制勾选时,给复选框传布尔 checked,并在 onChange 中根据 checked 更新状态。只需浏览器维护用户选择时,可以用 defaultChecked 给初始值,提交时读取表单。复选框的 value 不等于是否勾选,多选组通常保存一组稳定选项 ID,再用成员关系计算每项 checked。不要同时用 defaultChecked 和 checked 建立两套选择来源。
- 读取勾选布尔值用 checked,不用 value 猜测
- 多选集合保存稳定 ID,展示名称可以另行变化
- 全选状态从可选项和选中集合推导,避免重复保存
选中状态与提交字段不要混淆
假设通知设置里有邮件和短信两项,每个复选框的 value 分别是 email 和 sms。用户取消邮件勾选后,这个 input 的 value 仍然是 email,变化的是 checked。如果通用处理器只读取 value,就无法区分选中与取消,甚至可能把一个非空字符串始终当作真。
受控模式应由 React state 提供 checked,并同步处理用户的变更请求。若只传 checked 而既不提供更新逻辑也不明确 readOnly,用户点击后可能看见勾选被恢复。非受控模式则让浏览器维护当前状态,defaultChecked 不承担后续业务指令的同步责任。
多选列表用业务 ID 建模
权限选择可以保存 selectedIds 数组或集合,某一项的 checked 由是否包含它的 ID 决定。选中时加入,取消时移除,并创建新数组或集合交给 setter。不要直接修改旧 Set 后把同一个对象再传回去,否则状态身份没有正确表达变化,也容易影响旧快照。
选项重排后,数组索引不再稳定,所以不能把第一项是否选中当作某项权限的身份。异步加载选项或服务端删除权限时,还要定义原选择怎样处理:保留失效 ID 并提示,还是按业务规则移除。提交校验应使用当前有效选项与权限规则,不能只相信客户端曾经勾选过。
全选与半选是派生结果
全选按钮通常只作用于当前可操作集合,禁用项、分页之外的记录和已失效项需要明确产品语义。可以根据可选 ID 是否全部包含在 selectedIds 中推导全选,根据是否只选中其中一部分推导半选;不要额外保存一个容易与集合不同步的 isAllChecked。
浏览器原生复选框的 indeterminate 是独立 DOM 属性,表示视觉上的不确定状态,不会自动成为第三种提交值。组件库可能提供对应 prop,但底层语义仍要分清。验收时应覆盖选中、取消、全选、部分禁用和选项重排,确认显示与实际提交集合一致。
容易答错的地方
- 复选框用 value={true} 就能控制勾选
- value 表示表单项携带的值,控制勾选需要布尔 checked。把这两者混用,会使界面状态与提交参数难以对应。
- 表单没有某个 checkbox 字段,就一定是 false 字符串
- 原生表单通常不提交未勾选复选框对应的字段,并不会自动提交字符串 false。服务端与表单转换层需要明确缺失字段的业务含义。
面试官还会怎么问?
defaultChecked 改变后能覆盖用户选择吗?
它表达初始默认值,不能当成持续控制当前勾选的接口。需要外部状态实时控制时采用 checked;需要重置时明确表单重置或组件重建的范围。
多选项使用相同 name,怎样读取全部值?
使用 FormData.getAll(name) 获取重复字段。直接转成普通对象可能只保留某一个同名值,导致多个已选项在提交处理中丢失。
选项 ID 是数字,DOM value 会保留数字类型吗?
原生表单值通常以字符串形式读取。应在边界按约定校验和转换,或者统一使用字符串 ID,避免用数字集合比较字符串后发现所有选项都未选中。
参考资料
示例用于理解所注明的运行环境与边界;延伸学习可结合原文中的更多案例。