-
-
Notifications
You must be signed in to change notification settings - Fork 8.3k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Use Array.splice() to watch array #2116
Comments
You need to add a |
I find this api from : https://v3.vuejs.org/api/instance-methods.html#watch |
This is somewhat intended. Technically, I'm not sure if we should make v3's behavior strictly the same in this case, as it would entail the same tracking behavior when you do |
@yyx990803 I think we should keep the Vue 3 behavior as is. The Vue 2 behavior was out fo necessity. IU think with the old caveat out of the way, objects and arrays now behave the same - and both need a Artificially reverting to the Vue 2 behavior for arrays seems counterproductive in terms of a better, more unified API, even if it's a breaking change, technically. (probably not in the migration docs yet). The only thing giving me doubts is the IE11 build, where we the old behavior would necessarily surface again ... |
@LinusBorg but how are end users are going to benefit from the unified behaviour for both objects and arrays? I couldn't think of a case where we explicitly don't want to watch for array mutations, but I certainly can imagine cases where we don't want to watch for object mutations. Objects and arrays are used differently, so it might make sense to treat them differently as well to create a better developer experience overall, even though the behaviour might not be consistent. Let me provide you with an example of what I mean: const object = ref({ foo: 'bar' })
const array = ref(['foo']) Depending on a watched source I'd expect a different behaviour:
Lastly, I'd like to point out that render context has the right behaviour regarding arrays. When array is used in a |
I get your argument about differences in main use cases. At he same time, I also think when designing an API, providing consistent behaviors and and avoiding surprises should be an important factor as well. If feel like watching a ref should only trigger when the ref's value changes - not matter what kind the value of the ref might be. With your proposal, the default behavior would be different for arrays only, and people might not even be aware that their watchers run unnecessarily on every mutation of an array, when they might want to run it on each value reassignment only. Sure, once they are aware of this "weird" difference in behavior they can short-circuit the watcher by doing something like this: if (newArray === oldArray) return ...but that assumes they are even aware that it happens. Adding to that, in my personal experience at least, wrapping an array in a ref is usually done in situations where you do regularly replace the whole array, i.e. from API calls & refreshs, whereas I find myself using a Lastly, you can get the behavior you want (watching for array length changes, basically) with the current implementation at the small cost of a few more characters: const array = ref(['foo'])
watch(
() => [...array.value],
(newArray, oldArray) => {
// do something
}
) Here, you even get the benefit of having the previous array state to compare! (fiddle) So in summary:
|
Should the watch trigger if the reactive property is mutated then? const state = reactive({ items: ['foo'] })
watch(() => state.items, callback)
state.items.push('bar') // should it trigger watcher? |
Nope. For that, we have watch(() => [...state.items], callback) |
I guess in that case it should stop triggering watcher if I watch the state object only. const state = reactive({ items: ["foo"] })
watch(state, callback)
state.items.push('bar') // will trigger watcher But I still don't find it useful that it behaves exactly like objects, but maybe that's only me. |
Hm. Yeah tbh I wasn't event aware of this. it's implicitly doing a deep watch - personally I don't think that's a good thing, deep watches can be expensive :-/ I now realize it was probably done so people don't wonder why nothing happens when they watch pass an object by itself, but ... not sure I like it. |
anyway, the behaviour is consistent with objects and arrays, at least: /* REACTIVE */
const obj = reactive({})
const array = reactive([])
watch(obj, cb) // implicit deep: true
watch(array, cb) // implicit deep: true
obj.newProp = 'foo' // => triggers cb
array.push('foo') // => triggers cb
/* REFS */
const refObj = ref({})
const refArray = ref([])
watch(refObj, cb)
watch(refArray, cb)
refObj.value.newProp = 'foo' // => does *not* trigger cb
refArray.value.push('foo') // => does *not* trigger cb
/* FUNCTION */
const obj = reactive({})
const array = reactive([])
watch(() => obj, cb) // => does *not* trigger cb at all
watch(() => array, cb) // => does *not* trigger cb at all
watch(() => ({...obj}), cb) // => does trigger cb on mutation, but not deep
watch(() => [...array], cb) // => does trigger cb on mutation, but not deep. |
@yyx990803 I would propose to close this and leave the behaviour as is. WDYT? |
|
Yeah, took me around 20min to find out how I can workaround that |
If I add Check this |
Well, this is disappointing. I lost a few hours because, contrary to what the docs say, and contrary to all the logic, array.push() is "reactive" in the sense that my views are changed, yet I can't watch it as I always could. This is broken behaviour. |
agreed, I have spent a few hours teasing this out now too, not great. |
This may be a problem between how the data within the array. |
You are not required to do that. A custom getters allows you to have fine-grained control over any part of the process: // track array length only
watch(() => myArray.length, () => ...)
// or have the getter iterate over the array, i.e. with .slice()
// this would detect any structural change like a .sort() or .reverse(), where length would not change
watch(() => [...myArray], () => ...) Examples: |
This seems REALLY bad for large arrays for memory churn. This wouldn't even stop the spread early if it detected the first item had changed, it would still keep spreading all the way to the 100,000th item. |
Version
3.0.0-rc.10
Reproduction link
https://codepen.io/zmtlwzy/pen/oNxypGz
Steps to reproduce
Use Array.splice or sort to watch array in vue2 (can use),
but in vue3 cannot use.
vue2: https://codepen.io/zmtlwzy/pen/ExKRoNY
vue3: https://codepen.io/zmtlwzy/pen/oNxypGz
What is expected?
can watch in vue3
What is actually happening?
No effect
The text was updated successfully, but these errors were encountered: