Обходное решение обновления синхронизации MobX для флажка выбора / отмены выбора всех - PullRequest
0 голосов
/ 10 июня 2019

У меня есть список флажков, который в верхней части имеет флажок 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 голосов
/ 12 июня 2019

Я не уверен на 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>
    );
});

В более сложных настройках я подозреваю, что вы захотите отслеживать объекты с помощью идентификаторов, а не простых строк, поэтому решение, вероятно, будет также более сложным. Я думаю, что есть также довольно веские аргументы в пользу того, чтобы не поддерживать состояние формы в MobX, а скорее сохранять его в состоянии компонента, а если это требуется другим компонентам, отправлять текущий список выбранных элементов в хранилище (пустое или нет).

0 голосов
/ 10 июня 2019

Текущий обходной путь, который я нашел, чтобы упростить это, в случае lodash (в настоящее время использует его в любом случае) и _.constant или аналогичного подхода, где вы бы сделали:

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

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

С этого момента "freezes" Наблюдаемое значение и упрощает вещи.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...