选中/取消选中所有复选框的MobX同步可观察的更新解决方法

时间:2019-06-10 18:06:20

标签: javascript reactjs mobx mobx-react

我有一个复选框列表,其顶部有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更新了可观察对象,现在它是false,因为两个数组NOT相等
  5. 我们现在添加所有项目,并单击所有复选框。

最终结果是未取消选择任何内容,因为我们先删除了所有内容,然后又添加了所有内容。因此,需要该else

是否有一种方法可以重构此代码而无需执行else并且在代码中两次拥有removeChildren()?请注意,我要避免计算数组difference并添加/删除所需/缺少的元素。

在其中几乎有必要在注释旁边加上说明等。我可能缺少简单的东西或mobX的基本知识。

2 个答案:

答案 0 :(得分:0)

我发现简化此问题的当前解决方法是在lodash(无论如何目前都在使用它)和_.constant或类似的方法中进行的:

let hasAllChecked = _.constant(state.hasAllChecked)  // <-- freeze value

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

由于有了"freezes"这个可观察的值并简化了事情。

答案 1 :(得分:0)

我不确定是否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();

CodeSandbox

使用该组件的组件可能类似于:

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中不维护表单状态,而是将其保持在组件状态,如果其他组件需要它,则将当前选定项列表推送到商店(空或其他)也很有力。