Данные Immer не обновляются в Vue - PullRequest
2 голосов
/ 04 февраля 2020

Я пытаюсь использовать Immer с Vue. Похоже, что состояние обновляется, но Vue не обновляет пользовательский интерфейс

// immutable.js
import produce, { applyPatches } from "immer"

let undo_buffer = []
export var state = { items: [] }
const handle_add_patch = (patch, inverse_patches) => {
  console.log("Inverse Patches: ", inverse_patches)
  undo_buffer.push(inverse_patches)
}
export const add_item = (item_name) => {
  console.log("Starting add_item call")
  const next_state = produce(
    state,
    draft => {
      draft.items.push({ name: item_name })
    },
    handle_add_patch
  )
  console.log("next state: ", next_state)
  state = next_state
}
export const undo = () => {
  const undo_patch = undo_buffer.pop()
  if (!undo_patch) return
  let new_state = applyPatches(state, undo_patch)
  console.log("New State: ", new_state)
  state = new_state
}
<!-- item_list.Vue -->
<template>
  <div>
    <button @click.prevent="add_item()">Add Item</button>
      {{ items }}
    <button @click.prevent="undo()">Undo</button>
  </div>
</template>
<script>
import * as immutable from './immutable.js'
export default {
  computed: {
    items: function(){ return immutable.state.items }
  },
  methods: {
    add_item(){
      console.log("State Before: ", immutable.state)
      immutable.add_item("Hello")
      console.log("State After: ", immutable.state)
    },
    undo(){
      console.log("State Before: ", immutable.state)
      immutable.undo()
      console.log("State After: ", immutable.state)
    }
  }
}
</script>

. Console.log показывает, что массив элементов изменяется, но элементы в Vue Шаблон просто показывает пустой массив. Как я могу сделать это видимым в Vue?

Ответы [ 2 ]

3 голосов
/ 18 февраля 2020

Вычисленные свойства кэшируются и не пересчитываются до тех пор, пока не будут изменены данные связанных компонентов. Поскольку объект Immer не является частью компонента, повторное вычисление никогда не происходит.

Кэширование можно предотвратить, используя метод getter вместо вычисляемого свойства:

{{ getItems() }}

...

methods: {
  getItems: () => immutable.state.items
  addItem() {
    immutable.add_item("Hello");
    this.$forceUpdate();
  }
}

Более прямой подход заключается в принудительно пересчитать свойство:

data() {
  return { _itemsDirtyFlag: 0 }
},
computed: {
  items: {
    get() {
      this._itemsDirtyFlag; // associate it with this computed property
      return immutable.state.items;
    }
},
methods: {
  updateItems() {
    this._itemsDirtyFlag++;
  },
  addItem() {
    immutable.add_item("Hello");
    this.updateItems();
  }
}

Аналогичный подход, который использует Vue.util.defineReactive internal, используется vue-recomputed.

1 голос
/ 18 февраля 2020

Эта проблема не связана с Immer. vuejs кэширует вычисленные свойства, поэтому они не обновляются при обновлении ваших данных. Об этом ясно говорится в vuejs документах в https://vuejs.org/v2/guide/computed.html#Computed -Caching-vs-Methods

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

Поскольку Immer не является реактивной зависимостью, вычисляемое свойство никогда не запускается снова.

Решение для этого было бы изменить вычисляемое свойство на метод. Таким образом, он не будет кэшироваться, и метод будет запускаться каждый раз с обновленным значением.

...