Бесконечный цикл с обновленным атрибутом внутри события изменения базовой модели

1

Я использую Backbone и ReactJS, чтобы сохранить несколько HTML- div (позволяет вызывать их в виде ящиков), уложенных внутри другого div (позволяет вызвать этот контейнер). Пользователи могут увеличивать масштаб этого контейнера, который также увеличивает масштаб (не имеет ничего общего с функцией масштабирования CSS). Когда событие масштабирования запускается в контейнере, я вычисляю общую высоту увеличенных полей и обновляю контейнер с новой высотой стека. Фактический код является громоздким, следующий код похож на точный сценарий, который приводит к бесконечному циклу.

В следующем коде пользователь меняет значение масштабирования на модель ClickButton. Основываясь на значении zoom, я вычисляю новый j и обновляю его. Я также обновил k когда j изменяется. Теперь я нахожусь в бесконечном цикле с возрастающим значением k. Проверьте это на код, нажав кнопку "Кликнуть". Переменная k мчится до 100 (намеренно ломается на 100, иначе браузер замерзает)

Мое понимание: похоже, что когда я обновляю k или что-то еще внутри Backbone-модели на обработчике изменений, это создает другое событие изменения. Это новое событие начинается после завершения предыдущего события. Однако это новое событие по-прежнему возвращает true для вызова функции this.hasChanged("j"), который произошел до предыдущего события и обрабатывался.

Текущее решение: я использую опцию {silent: true} в качестве входного сигнала для метода set базовой линии и успешно могу остановить бесконечный цикл.

Я хотел бы еще один способ сделать это, не подавляя события изменения, поскольку мое обновление HTML-страницы зависит от изменений событий. Приложение My ReactJS подключилось к модели и обновило представление об изменении модели.

Кто-нибудь знает лучшее решение?

var ClickButton = Backbone.Model.extend({
  defaults: function() {
    return {
      className: "display",
      zoom: 0,
      j: 0,
      k: 0,
      children: [],
    }
  },
  initialize() {
    getElement("zoom").value = this.get("zoom");
    getElement("zoomchanged").value = 0;
    this.on("change", function(e) {
      if (this.hasChanged("j")) {
        getElement("j").value = this.get("zoom");
        if (this.get("k") < 100) {
          // Just added hundred that your browser wont get stuck
          this.set("k", parseInt(this.get("k")) + 1);
          getElement("k").value = parseInt(this.get("k"));
        }
      }
      if (this.hasChanged("zoom")) {
        getElement("zoomchanged").value = parseInt(getElement("zoomchanged").value) + 1;
        console.log(this.get("zoom"));
        getElement("zoom").value = this.get("zoom");
        this.set("j", this.get("zoom"));
      }
    });
  }
});

var clickButton = new ClickButton();

var button = getElement("button");
button.addEventListener("click", (function(e) {
  var currentValueOfZoom = clickButton.get("zoom");
  clickButton.set("zoom", currentValueOfZoom + 1);
}).bind(this));

// Helpers
function getElement(className) {
  return document.getElementsByClassName(className)[0];
}
input {
  width: 35px;
}

div#holder {
  display: flex;
}

div#holder input {
  margin-right: 23px;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/underscore.js/1.8.3/underscore.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/backbone.js/1.3.3/backbone.js"></script>
<div id="holder">
  <label>K: </label><input class="k" type="text"><br>
  <label>J: </label><input class="j" type="text"><br>
  <label>zoom: </label><input class="zoom" type="text"><br>
  <label>Number of times zoom changed: </label><input class="zoomchanged" type="text"><br>
  <div class="display">

  </div>
  <button class="button">
  Click
  </button>
</div>
Теги:
infinite-loop
backbone.js
backbone-events

2 ответа

1
Лучший ответ

Такое поведение можно было бы предотвратить с Backbone silent:true вариант. Однако, как отмечено в документации, лучшим решением было бы передать определенный флаг (например, флаг ignore: true который я передаю и проверяю в приведенном ниже фрагменте).

Из базовых документов:

Вообще говоря, при вызове функции, которая испускает событие (model.set, collection.add и т.д.), Если вы хотите предотвратить запуск события, вы можете передать {silent: true}, как опция. Обратите внимание, что это редко, возможно, даже никогда, хорошая идея. Передача определенного флага в вариантах обратного вызова вашего события для просмотра и выбора игнорирования обычно будет работать лучше.

this.on("change", function(e, options){
    if(options.ignore !== true && this.hasChanged("zoom")){
        getElement("zoomchanged").value = parseInt(getElement("zoomchanged").value) + 1;
        console.log(this.get("zoom"));
        getElement("zoom").value = this.get("zoom");
        this.set("j", this.get("zoom"), {ignore: true});
    }
});
  • 0
    Благодарю. Это решает проблему. +1, так как я никогда не знал, что могу передать пользовательские данные в качестве параметров. Я всегда думал, что параметры похожи на инструкции для внутренних компонентов Backbone, такие как {silent: true}, которые будут подавлены при изменении события.
0

В то время как lucasjackson предоставляет рабочий патч, специфичный для вашей ситуации, я хотел бы поделиться лучшими практиками в любом случае.

Что происходит?

.hasChanged возвращает только последние изменения, сделанные с помощью set и не отслеживает никаких других последующих изменений, если они не сделаны в связанном событии запуска. После того, как set полностью сделан, любой последующий вызов set перепишет changed свойство.

Поскольку вы изменяете zoom, j и k внутри общего события change, любой последующий set запускает новое событие change где hasChanged всегда возвращает true, поскольку changed свойство выглядит следующим образом:

{
    zoom: 1,
    j: 1,
    k: 100,
}

Как решить этот бесконечный цикл?

Для упрощения и улучшения вашей модели можно сделать несколько вещей. Во-первых, не играйте с DOM внутри базовой модели. Это работа для представления, так что действуйте в вашем случае.

Имея это в виду, используйте специальные обработчики событий change:[attribute] (change:[attribute]).

var ClickButton = Backbone.Model.extend({
    defaults: {
        zoom: 0,
        j: 0,
        k: 0,
    },
    initialize() {
        this.listenTo(this, {
            'change:zoom': this.onZoomChange,
            'change:j': this.onJChange
        });
    },
    onZoomChange(model, value, options) {
        this.set("j", this.get("zoom"));
    },
    onJChange(model, value, options) {
        this.set("k", parseInt(this.get("k")) + 1);
    }
});

При этом я не уверен, что модель Backbone - хороший выбор для нажатия кнопки. Но опять же, вы не поделились кодом React...

Ещё вопросы

Сообщество Overcoder
Наверх
Меню