如何使用反应钩子实现多个复选框

Posted

技术标签:

【中文标题】如何使用反应钩子实现多个复选框【英文标题】:How to implement multiple checkbox using react hook 【发布时间】:2019-10-09 21:35:02 【问题描述】:

我想使用 react-hook 在我的 html 页面上实现多个复选框。

我尝试使用此 URL 实现:https://medium.com/@Zh0uzi/my-concerns-with-react-hooks-6afda0acc672。在提供的链接中,它是使用类组件完成的并且工作正常,但是每当我使用 React hook setCheckedItems 更新复选框选中状态时,它都不会重新渲染视图。

第一次渲染视图并从 Checkbox 组件打印 console.log()。单击复选框功能后,handleChange 被调用并 checkedItems 更新值,但视图不再呈现(没有 console.log() 打印)。并且 checkedItems.get("check-box-1") 也没有打印任何值。

下面是我的示例代码。

复选框示例

import React,  useState  from 'react';
import Checkbox from '../helper/Checkbox';

const CheckboxExample = () => 
    const [checkedItems, setCheckedItems] = useState(new Map());

    const handleChange = (event) => 
        setCheckedItems(checkedItems => checkedItems.set(event.target.name, event.target.checked));
        console.log("checkedItems: ", checkedItems);
    

    const checkboxes = [
        
            name: 'check-box-1',
            key: 'checkBox1',
            label: 'Check Box 1',
        ,
        
            name: 'check-box-2',
            key: 'checkBox2',
            label: 'Check Box 2',
        
    ];


    return (
        <div>
            <lable>Checked item name : checkedItems.get("check-box-1") </lable> <br/>
            
                checkboxes.map(item => (
                    <label key=item.key>
                        item.name
                        <Checkbox name=item.name checked=checkedItems.get(item.name) onChange=handleChange />
                    </label>
                ))
            
        </div>
    );

export default Example;

复选框

import React from 'react';

const Checkbox = ( type = 'checkbox', name, checked = false, onChange ) => 
    console.log("Checkbox: ", name, checked);

  return (<input type=type name=name checked=checked onChange=onChange /> )

export default Checkbox;

【问题讨论】:

【参考方案1】:

我不认为使用Map 来代表状态是最好的主意。 我已经使用普通的Object 实现了您的示例,并且可以正常工作:

https://codesandbox.io/s/react-hooks-usestate-xzvq5

const CheckboxExample = () => 
  const [checkedItems, setCheckedItems] = useState(); //plain object as state

  const handleChange = (event) => 
      // updating an object instead of a Map
      setCheckedItems(...checkedItems, [event.target.name] : event.target.checked );
  

  useEffect(() => 
    console.log("checkedItems: ", checkedItems);
  , [checkedItems]);  

  const checkboxes = [
      
          name: 'check-box-1',
          key: 'checkBox1',
          label: 'Check Box 1',
      ,
      
          name: 'check-box-2',
          key: 'checkBox2',
          label: 'Check Box 2',
      
  ];


  return (
      <div>
          <lable>Checked item name : checkedItems["check-box-1"] </lable> <br/>
          
              checkboxes.map(item => (
                  <label key=item.key>
                      item.name
                      <Checkbox name=item.name checked=checkedItems[item.name] onChange=handleChange />
                  </label>
              ))
          
      </div>
  );

编辑:

原来Map 可以用作状态值,但要触发重新渲染,您需要用新的 Map 替换而不是简单地改变它,这不是 React 选择的,即:

const handleChange = (event) => 
  // mutate the current Map
  checkedItems.set(event.target.name, event.target.checked)
  // update the state by creating a new Map
  setCheckedItems(new Map(checkedItems) );
  console.log("checkedItems: ", checkedItems);

但在这种情况下,我认为使用 Map 没有任何好处,除了使用 .get().set() 而不是 x[y] 的更简洁的语法。

【讨论】:

这会启动他的渲染吗?看起来他正在使用 checkedItems 状态变量进行渲染,但他的代码看起来只是在更新 setCheckedItems 状态变量。不过我可能会误解它 @abelito 如果您运行链接的演示,您将清楚地看到 checked 属性对应于状态,即 ckeckbox 显示为已选中以响应状态更新。 啊,我现在看到了,太好了! @pawel 感谢您的快速回复。使用这个有效的对象。我想知道为什么它不能使用 Map。 @Nakesh 这可能与 React 内部有关。我确定MapSet 在 Redux 商店中使用时效果不佳,这就是我从这个角度解决您的问题的原因。不确定是否有官方消息称不应在 React 组件状态下使用它们,但看看差异,在您的情况下 Map over Object 似乎没有任何好处。【参考方案2】:

看起来有点长,但是如果您将地图展开并将其应用于new Map,您的组件将重新渲染。我认为使用 Object 引用而不是 Map 在这里效果最好。

const useState = React

const Mapper = () => 
  const [map, setMap] = useState(new Map());

  const addToMap = () => 
    const RNDM = Math.random().toFixed(5)
    map.set(`foo$RNDM`, `bar$RNDM`);
    setMap(new Map([...map]));
  

  return (
    <div>
      <ul>
        [...map].map(([v, k]) => (
          <li key=k>
            k : v
          </li>
        ))
      </ul>
      <button onClick=addToMap>add to map</button>
    </div>
  );
;

const rootElement = document.getElementById("react");
ReactDOM.render(<Mapper />, rootElement);
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/16.8.4/umd/react.production.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/16.8.4/umd/react-dom.production.min.js"></script>
<div id="react"></div>

【讨论】:

【参考方案3】:

作为使用单个对象来保存多个项目状态的补充,如果在单个渲染中更新多个项目,更新将不会按预期发生。渲染周期内更新的提交将简单覆盖之前的提交。

解决方案是将所有更改集中在一个对象中并一次性提交,如下所示:

// An object that will hold multiple states
const [myStates, setMyStates] = useState();

// An object that will batch all the desired updates
const statesUpdates = ;

// Set all the updates...
statesUpdates[state1] = true;
statesUpdates[state2] = false;
// etc...

// Create a new state object and commit it
setMyStates(Object.assign(, myStates, statesUpdates));

【讨论】:

可以,或者使用函数更新状态:reactjs.org/docs/hooks-reference.html#functional-updates【参考方案4】:

作为Map 的替代方案,您可以考虑使用Set。然后您不必担心最初将每个项目设置为false 以表示未选中。快速 POC:

    const [selectedItems, setSelectedItems] = useState(new Set())

    function handleCheckboxChange(itemKey: string) 
        // first, make a copy of the original set rather than mutating the original
        const newSelectedItems = new Set(selectedItems)
        if (!newSelectedItems.has(itemKey)) 
            newSelectedItems.add(itemKey)
         else 
            newSelectedItems.delete(itemKey)
        
        setSelectedItems(newSelectedItems)
    

...

    <input
        type="checkbox"
        checked=selectedItems.has(item.key)
        onChange=() => handleCheckboxChange(item.key)
    />

【讨论】:

【参考方案5】:

export default function Input(props) 
    const 
        name,
        isChecked,
        onChange,
        index,
     = props;

    return (
        <>
            <input
                className="popup-cookie__input"
                id=name
                type="checkbox"
                name=name
                checked=isChecked
                onChange=onChange
                data-action=index
            />
            <label htmlFor=name className="popup-cookie__label">name</label>
        </>
    );


const checkboxesList = [
    name: 'essential', isChecked: true,
    name: 'statistics', isChecked: false,
    name: 'advertising', isChecked: false,
];

export default function CheckboxesList() 
    const [checkedItems, setCheckedItems] = useState(checkboxesList);

    const handleChange = (event) => 
        const newCheckboxes = [...checkedItems];
        newCheckboxes[event.target.dataset.action].isChecked = event.target.checked;
        setCheckedItems(newCheckboxes);
        console.log('checkedItems: ', checkedItems);
    ;

    return (
        <ul className="popup-cookie-checkbox-list">
            checkboxesList.map((checkbox, index) => (
                <li className="popup-cookie-checkbox-list__item" key=checkbox.name>
                    <Input
                        id=checkbox.name
                        name=checkbox.name
                        isChecked=checkbox.isChecked
                        onChange=handleChange
                        index=index
                    />
                </li>
            ))
        </ul>
    );
```

【讨论】:

以上是关于如何使用反应钩子实现多个复选框的主要内容,如果未能解决你的问题,请参考以下文章

检查另一个输入时如何根据一个输入更改值?反应钩子

如何在反应钩子中使用graphql钩子集成多个客户端

如何使用反应钩子执行多个异步请求?

如何使用反应钩子 react.js 一次更新多个状态

动态表单 - 如何使用反应钩子更新“onChange”事件中多个表单字段的值?

制作自定义反应钩子以执行查询和突变