首页 > 解决方案 > 选择/取消选择所有复选框的 MobX 同步可观察更新解决方法

问题描述

我有一个复选框列表,顶部有Select All复选框。它由一个observable array字符串驱动。当用户检查它时,Select All它应该显示check所有复选框,然后单击它应该取消全选。很直截了当。

有关状态等的一些详细信息:

let arrayB = ['a','b','c'] // List coming from an API etc

let state = observable({
   arrayA: [],  // <-- nothing is checked by default
   get hasAllChecked() {
    return ( state.arrayA.length === arrayB.length )
  },
})

问题是我很好奇的是复选框列表的这个处理程序onChange

if (!state.hasAllChecked) {
  removeChildren()
} else {
  removeChildren()
  addAllChildren()
}

乍一看,您会问为什么不将其重构为:

removeChildren() 
if (!state.hasAllChecked)
  addAllChildren()

以下是说明原因和步骤:

  1. 复选框是明确的,没有任何检查。您单击并没有选中所有框。
  2. 到目前为止一切都很好。现在用户再次单击Deselect All复选框(相同的框只是标签根据hasAllChecked值更改)
  3. 既然我们现在没有if,我们继续前进,remove所有项目
  4. 在幕后 MobX 更新了 observable,现在是false因为两个数组NOT相等
  5. 我们现在添加所有项目并单击所有复选框。

最终结果是没有取消选择,因为我们删除了所有然后添加了所有。因此需要这样做else

有没有一种方法可以在代码中不做两次的情况下重构else它?removeChildren()请注意,我想避免计算数组difference并​​添加/删除需要/缺少的元素。

将它放在那里几乎需要在其旁边添加注释并附上解释等。我可能缺少一些简单的东西或 mobX 等基本的东西。

标签: javascriptreactjsmobxmobx-react

解决方案


我不是 100% 确定我了解您的要求,但这是一个通过简单商店操作选择的演示。希望这可能有助于澄清事情!

const items = ["one", "two", "three", "four", "five"];

const store = {
    checked: [],

    checkAll() {
        this.checked.replace(items);
    },

    get hasAllChecked() {
      return this.checked.length === items.length;
    },

    isChecked(item) {
        return this.checked.includes(item);
    },

    selectItem(item) {
      this.isChecked(item) ? this.checked.remove(item) : this.checked.push(item)
    },

    uncheckAll() {
        this.checked.clear();
    }
};

decorate(store, {
    checked: observable,
    checkAll: action,
    selectItem: action,
    uncheckAll: action
});

const toggleSelectAll = () =>
    store.hasAllChecked ? store.uncheckAll() : store.checkAll();

代码沙盒

使用它的组件可能看起来像:

const renderCheckboxItem = (item, i) => {
    const checked = store.isChecked(item);
    return (
        <React.Fragment key={`item-${i}`}>
            <input
                name={`item-${item}`}
                type="checkbox"
                checked={checked}
                onChange={() => store.selectItem(item)}
            />
            <label htmlFor={`item-${item}`}>{item}</label>
        </React.Fragment>
    );
};

const renderItem = (item, i) => <li key={`selected-item-${i}`}>{item}</li>;

const Selector = observer(() => {
    return (
        <React.Fragment>
            <form>
                {items.map(renderCheckboxItem)}
                <React.Fragment key="item-select-all">
                    <input
                        name="select-all"
                        type="checkbox"
                        onChange={toggleSelectAll}
                    />
                    <label htmlFor="select-all">Select all</label>
                </React.Fragment>
            </form>

            <p>Selected items:</p>
            <ul>{store.checked.map(renderItem)}</ul>
        </React.Fragment>
    );
});

在更复杂的设置中,我怀疑您希望使用 id 而不是简单的字符串来跟踪对象,因此解决方案也可能更复杂。我认为在 MobX 中不维护表单状态,而是将其保持在组件状态,如果其他组件需要它,将当前选定项目的列表推送到商店(为空或其他),这也是一个非常有力的案例。


推荐阅读