【问题标题】: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;

【问题讨论】:

    标签: javascript reactjs checkbox react-hooks


    【解决方案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));
      

      【讨论】:

      【解决方案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>
            );
        }```
        

        【讨论】:

          猜你喜欢
          • 1970-01-01
          • 1970-01-01
          • 2020-02-03
          • 1970-01-01
          • 2022-01-02
          • 2020-01-30
          • 2019-12-18
          • 1970-01-01
          • 1970-01-01
          相关资源
          最近更新 更多