ReactJS:使用 useState 管理多个复选框输入 [英] ReactJS: Manage multiple checkbox inputs with useState

查看:25
本文介绍了ReactJS:使用 useState 管理多个复选框输入的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我有以下示例组件,它使用多个复选框来选择要从对象列表中删除的项目:

import React, { useState } from "react";导入./styles.css";常量数据 = [{名称:测试1",结果:通过"},{名称:测试2",结果:通过"},{名称:test3",结果:通过"},{名称:test4",结果:通过"},{名称:测试5",结果:通过"}];导出默认函数 App() {const [allChecked, setAllChecked] = useState(false);const [isChecked, setIsChecked] = useState({});const [formData, setFormData] = useState(data);const handleAllCheck = e =>{setAllChecked(e.target.checked);};const handleSingleCheck = e =>{setIsChecked({ ...isChecked, [e.target.name]: e.target.checked });};const onDelete = () =>{console.log(isChecked);const newData = data.filter(项目 =>!Object.keys(isChecked).includes(item.name));控制台日志(新数据);setFormData(newData);};返回 (<div className="应用程序"><div><label>All</label><输入名称=checkall"类型=复选框"已检查={allChecked}onChange={handleAllCheck}/><标签/>

{formData.map((test, index) => (<div key={index}><label>{test.name}</label><输入类型=复选框"名称={test.name}已检查={allChecked ?true : isChecked[test.name]}onChange={handleSingleCheck}/>

))}<button onClick={() =>onDelete()}>DELETE</button>

);}

这主要是有效的,除了检查所有.在使用 useState 时,onChange 似乎不会更新.我需要能够选择所有对象或取消选中某些对象以标记为删除.

非常感谢任何帮助.

CodeSandbox 示例:https://codesandbox.io/s/modest-hodgkin-kryco

更新:

好的,在 Richard Matsen 的帮助下,

这是一个没有直接 DOM 操作的新解决方案:

import React, { useState, useEffect } from "react";导入./styles.css";常量数据 = [{名称:测试1",结果:通过"},{名称:测试2",结果:通过"},{名称:test3",结果:通过"},{名称:test4",结果:通过"},{名称:测试5",结果:通过"}];导出默认函数 App() {const [allChecked, setAllChecked] = useState(false);const [isChecked, setIsChecked] = useState();const [loading, setLoading] = useState(true);const [formData, setFormData] = useState(data);const handleAllCheck = e =>{setAllChecked(e.target.checked);};const handleSingleCheck = e =>{setIsChecked({ ...isChecked, [e.target.name]: e.target.checked });};const onDelete = () =>{const itemList = Object.keys(isChecked).map((key:any) => {if (isChecked[key] === true) {返回键}})const 结果 = formData.filter((item:any) => !itemList.includes(item.name))控制台日志(结果)设置表格数据(结果)}useEffect(() => {如果(!加载){setIsChecked(当前 => {const nextIsChecked = {}Object.keys(current).forEach(key => {nextIsChecked[key] = allChecked;})返回 nextIsChecked;});}}, [allChecked, loading]);useEffect(() => {const initialIsChecked = data.reduce((acc,d) => {acc[d.name] = false;返回acc;}, {})setIsChecked(initialIsChecked)设置加载(假)}, [加载])返回 (<div className="应用程序"><div><label>All</label><输入名称=checkall"类型=复选框"已检查={allChecked}onChange={handleAllCheck}/><标签/>

{!加载?formData.map((test, index) => (<div key={index}><label>{test.name}</label><输入类型=复选框"名称={test.name}已检查={isChecked[test.name]}onChange={handleSingleCheck}/>

)): 空值}<button onClick={() =>onDelete()}>DELETE</button>

);}

工作解决方案的代码沙盒:https://codesandbox.io/s/happy-rubin-5zfv3

解决方案

基本问题是 checked={allChecked ?true : isChecked[test.name]} 停止取消检查操作的发生 - 一旦 allChecked 为真,什么值都无关紧要 isChecked[test.name]有,这个表达式永远是真的.

您应该只依赖 isChecked 作为值,并将更改 allChecked 视为副作用.

 useEffect(() => {setIsChecked(当前 => {const nextIsChecked = {}Object.keys(current).forEach(key => {nextIsChecked[key] = allChecked;})返回 nextIsChecked;});}, [allChecked]);...{formData.map((test, index) => (<div key={index}><label>{test.name}</label><输入类型=复选框"名称={test.name}已检查={isChecked[test.name]}onChange={handleSingleCheck}/>

))}

还有这个警告出现

<块引用>

警告:组件正在更改要控制的复选框类型的不受控制的输入.输入元素不应从不受控制切换到受控制(反之亦然).决定在组件的生命周期内使用受控或非受控输入元素.

所以这基本上是说不要将isChecked 初始化为{},因为输入的checked 属性最初是未定义的.改用这个,

{测试1:假,测试2:假,测试3:假,测试4:假,测试5:假,}

或者这样

const data = { ... }const initialIsChecked = data.reduce((acc,d) => {acc[d.name] = false;返回acc;}, {})导出默认函数 App() {const [allChecked, setAllChecked] = useState(false);const [isChecked, setIsChecked] = useState(initialIsChecked);...

I have the following example component that uses multiple checkboxes for choosing what items to remove from a list of objects:

import React, { useState } from "react";
import "./styles.css";

const data = [
  {
    name: "test1",
    result: "pass"
  },
  {
    name: "test2",
    result: "pass"
  },
  {
    name: "test3",
    result: "pass"
  },
  {
    name: "test4",
    result: "pass"
  },
  {
    name: "test5",
    result: "pass"
  }
];

export default function App() {
  const [allChecked, setAllChecked] = useState(false);
  const [isChecked, setIsChecked] = useState({});
  const [formData, setFormData] = useState(data);

  const handleAllCheck = e => {
    setAllChecked(e.target.checked);
  };

  const handleSingleCheck = e => {
    setIsChecked({ ...isChecked, [e.target.name]: e.target.checked });
  };

  const onDelete = () => {
    console.log(isChecked);
    const newData = data.filter(
      item => !Object.keys(isChecked).includes(item.name)
    );
    console.log(newData);
    setFormData(newData);
  };

  return (
    <div className="App">
      <div>
        <label>All</label>
        <input
          name="checkall"
          type="checkbox"
          checked={allChecked}
          onChange={handleAllCheck}
        />
        <label />
      </div>
      {formData.map((test, index) => (
        <div key={index}>
          <label>{test.name}</label>
          <input
            type="checkbox"
            name={test.name}
            checked={allChecked ? true : isChecked[test.name]}
            onChange={handleSingleCheck}
          />
        </div>
      ))}
      <button onClick={() => onDelete()}>DELETE</button>
    </div>
  );
}

This is mostly working, except for check all. It seems onChange will not update while using useState. I need to be able to select all the objects or uncheck some to mark for deletion.

Any help is greatly appreciated.

CodeSandbox Example: https://codesandbox.io/s/modest-hodgkin-kryco

UPDATE:

Okay, after some help from Richard Matsen,

Here is a new solution without direct DOM manipulation:

import React, { useState, useEffect } from "react";
import "./styles.css";

const data = [
  {
    name: "test1",
    result: "pass"
  },
  {
    name: "test2",
    result: "pass"
  },
  {
    name: "test3",
    result: "pass"
  },
  {
    name: "test4",
    result: "pass"
  },
  {
    name: "test5",
    result: "pass"
  }
];

export default function App() {
  const [allChecked, setAllChecked] = useState(false);
  const [isChecked, setIsChecked] = useState();
  const [loading, setLoading] = useState(true);
  const [formData, setFormData] = useState(data);

  const handleAllCheck = e => {
    setAllChecked(e.target.checked);
  };

  const handleSingleCheck = e => {
    setIsChecked({ ...isChecked, [e.target.name]: e.target.checked });
  };

  const onDelete = () => {
      const itemList = Object.keys(isChecked).map((key:any) => {
        if (isChecked[key] === true) {
          return key
        }
      })
      const result = formData.filter((item:any) => !itemList.includes(item.name))
      console.log(result)
      setFormData(result)
    }

  useEffect(() => {
    if (!loading) {
    setIsChecked(current => {
      const nextIsChecked = {}
      Object.keys(current).forEach(key => {
        nextIsChecked[key] = allChecked;
      })
      return nextIsChecked;
    });
    }
  }, [allChecked, loading]);

  useEffect(() => {
    const initialIsChecked = data.reduce((acc,d) => {
      acc[d.name] = false;
      return acc;
    }, {})
    setIsChecked(initialIsChecked)
    setLoading(false)
  }, [loading])

  return (
    <div className="App">
      <div>
        <label>All</label>
        <input
          name="checkall"
          type="checkbox"
          checked={allChecked}
          onChange={handleAllCheck}
        />
        <label />
      </div>
      {!loading ? formData.map((test, index) => (
      <div key={index}>
        <label>{test.name}</label>
        <input
          type="checkbox"
          name={test.name}
          checked={isChecked[test.name]}
          onChange={handleSingleCheck}
        />
      </div>
      )): null}
      <button onClick={() => onDelete()}>DELETE</button>
    </div>
  );
}

codesandbox of working solution: https://codesandbox.io/s/happy-rubin-5zfv3

解决方案

The basic problem is checked={allChecked ? true : isChecked[test.name]} stops the unchecking action from happening - once allChecked is true it does not matter what value isChecked[test.name] has, the expression is always going to be true.

You should rely only on isChecked for the value, and treat changing allChecked as a side-effect.

  useEffect(() => {
    setIsChecked(current => {
      const nextIsChecked = {}
      Object.keys(current).forEach(key => {
        nextIsChecked[key] = allChecked;
      })
      return nextIsChecked;
    });
  }, [allChecked]);

  ...

  {formData.map((test, index) => (
    <div key={index}>
      <label>{test.name}</label>
      <input
        type="checkbox"
        name={test.name}
        checked={isChecked[test.name]}
        onChange={handleSingleCheck}
      />
    </div>
  ))}

There's also this warning cropping up

Warning: A component is changing an uncontrolled input of type checkbox to be controlled. Input elements should not switch from uncontrolled to controlled (or vice versa). Decide between using a controlled or uncontrolled input element for the lifetime of the component.

So that's basically saying don't initialize isChecked to {}, because the input's checked property is initially undefined. Use this instead,

{
  test1: false,
  test2: false,
  test3: false,
  test4: false,
  test5: false,
}

or this way

const data = { ... }

const initialIsChecked = data.reduce((acc,d) => {
  acc[d.name] = false;
  return acc;
}, {})

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

这篇关于ReactJS:使用 useState 管理多个复选框输入的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
相关文章
其他开发最新文章
热门教程
热门工具
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆