So I have been learning the Vue Composition API and was wondering what the difference between watchEffect and watch is. Watch says it's the same as the Vue 2 watch, so I'm guessing watchEffect is like the 2.0 of that? I'm wondering if there is any specific cases where one would have great advantages over the other like in the case of stopping the watchEffect and then reactivating it instead of using a boolean in a regular watch... or are they just basically different ways of writing the same thing.
Thanks!
Reference:
watcheffect: https://vue-composition-api-rfc.netlify.com/api.html#watcheffect
watch: https://vue-composition-api-rfc.netlify.com/api.html#watch
watchEffect seems to be a simplified watch and the main differences are
Only accepts a function
watch can accept either a function or one or more reactive properties.
Runs immediately when defined and when reactive dependencies change
watch only runs when reactive dependencies change
What helped me to understand the difference between watch and watchEffect in Vue 3 was to think about watchEffect as computed with side-effects.
The watchEffect() hook works like the computed() hook or the computed option, but instead of returning a value, you use it to trigger side-effects.
Use watch whenever you want to trigger a side-effect when a single reactive value changes.
// Triggers whenever `user` is updated.
watch(user, () => doSomething({ user: user.value, profile: profile.value }))
Use watchEffect whenever you need to watch multiple reactive values and trigger a side-effect whenever any of them is updated.
// Triggers whenever `user` *or* `profile` is updated.
watchEffect(() => doSomething({ user: user.value, profile: profile.value }))
See: watch vs. watchEffect when to use what with Vue.js
I would use:
watchEffect when I want to watch multiple reactive properties and I don't care about old values
watch when I want to watch one specific reactive properties and I may want old value
Note, above is what I would use them for, but may not be their only usage.
Also found in docs regarding the difference:
Compared to watchEffect, watch allows us to:
Perform the side effect lazily;
Be more specific about what state should trigger the watcher to re-run;
Access both the previous and current value of the watched state.
Source: https://composition-api.vuejs.org/api.html#watch
watchEffect is something introduced in Vue3 with its composition api. The reason to have both watchEffect and watch, as I understand, is to keep the semantics of watch as close as possible to that of Vue2. The birth of watchEffect, if you are interested, can be traced back to here and here
As it stands today, watchEffect is an immediate/eager watch that uses a more concise/consistent syntax (consistent with computed):
watchEffect does not accept explicit watch sources, but instead automatically figures out all the dependencies by immediately executing the callback (or effect as how it is called in the source code), similar to how computed works. Therefore watchEffect must run the effect immediately. And because of this, there is a common trap (at least I have to keep reminding myself of it constantly) when setting up watchEffect: you need to make sure that during the first execution of your watchEffect, all of the dependencies are indeed accessed. How would some dependency escape from being accessed? Watch for conditional statements.
watchEffect will run its effect immediately as mentioned above.
watchEffect is a deep watch. This is something I am not sure whether it is intended or not. If you use a reactive object inside your effect, any change on that object will cause the effect to rerun, even if the changed property is not the one you accessed or is nested.
If Vue 3 is designed from scratch or there is no concern of maintaining backward compatibility, I would imagine there will only be watchEffect
Related
I'm trying to write SSR-friendly code, keeping in mind a potential migration of my app from SPA to SSR. One of the principles of such code is to avoid statefull singletons which can cause cross request state pollution
The problem comes when I need to use an instance of Vuex, Vue-router, Vue-i18n etc. outside of Vue component. Because the solution in all the respective SO answers is... You guessed it. To create and export a statefull singleton in a separate js file and then import it all over the app:
Vue-router: https://stackoverflow.com/a/43562210/11208064
Vuex: https://stackoverflow.com/a/47575742/11208064
Vue-i18n: https://stackoverflow.com/a/66695008/11208064
A simple example
// store.js
export default new Vuex.Store() {} // this is a singleton
// someOtherModule.js
import { store } from './store.js'
Pinia has covered the issue in the docs. They recommend to access the pinia instance via app.$pinia. However they don't specify how do I access the app instance itself.
Also the problem is not limited to these libraries. We write our own modules which work in a similar manner.
Researching the issue I came across this article. It suggests to wipe module cache, so each time you require it all the code is executed again, making it fresh and stateless. Sounds pretty sophisticated, but maybe this is the way? And if not, what is the recommended solution?
I'm working on unit testing all of my components, and I've hit a bit of a snag (or maybe a misunderstanding of how to properly test).
I have a component that receives some parameters from the store, and then calls a series of functions that mostly return an input string with some transformations applied.
This looks something like the following:
<script setup lang="ts">
import { someTextTransform } from '../utilities/someTextTransform';
...
let parsedText = store.input;
if (store.applySomeTextTransform) {
parsedText = someTextTransform(parsedText);
}
I have already tested these functions themselves, so to me it seems like testing the actual output of the calls is unnecessary and I instead could just test to see if the functions have been called.
Here is a snippet on spying from the Vitest docs
Mocking functions can be split up into two different categories; spying & mocking.
Sometimes all you need is to validate whether or not a specific function has been called (and possibly which arguments were passed). In these cases a spy would be all we need which you can use directly with vi.spyOn().
This is exactly what I want. I want only to know whether or not the function has been called. The functions themselves are simple enough that they do not require additional setup (i.e., mocking is not required).
However looking through the docs of Vue Test Utils I don't see a way to do this. It looks like a more common way to do this is to mock the functions first and then you can spy on them more easily, but then I would have to create mocks that don't actually mock the functions which seems like I'm adding code to my codebase for no benefit other than a weird workaround.
I may just be missing something on how to do this, but I might also be approaching the testing in a suboptimal way so any advice is appreciated.
Overview:
I refactoring script tests before I was used Enzyme to test, but now, I want to use #testing-library/react
Problem:
I can't find a solution for setState in #testing-library/react
Using setState is dangerous approach regardless testing library used.
It depends on implementation details(say, property names inside the state) so it becomes much harder to maintain tests - more tests to change, easy to get test broken when app is fine etc.
You cannot call that once you convert class component to functional one with hooks. So you depends on implementation details even more.
And finally direct state manipulation may end with state you would never get in real world. This means your component will be broken because it's impossible to reach some state but your tests with direct initialization will be fine.
So what you better do? Provide props, change props, call props(wrapper.find('button').filter(button => button.text() === 'Cancel').props().onClick() for enzyme, fireEvent.click(getByText(/Cancel/i)) for RTL) and verify against what's rendered.
This way your tests will be shorter, most actual and need less changes after you update component under test.
As I am trying to play with evolutionary algorithms and building visualization tools of the process, I'd like to use VueJs with P5Js.
I have this code: (SO) Playing with Evolutionary Algorithms on Codepen.io
The problem is that Vue instance's data population does not update properly.
Here what I tried to do:
Pass the global objet Game as Vue instance's data. Then population get updated properly, but the data is so big that everything get laggy;
Populate Game.population array when declared, but it does not work: P5JS throw me an error createVector() is not defined;
Populate Game.population array in Vue instance's beforeCreate() method. It does not work neither. Giving me an other error;
Proxy Game.population by an other object Data declared globally or in Vue instance's beforeCreate() method, for curating interesting cells data (I don't need the pos attribute) and so avoiding createVector() error. It is a dead end;
Thinking about hoisting, I wondered if VueJS was initialized before P5JS run. It is the case. But I can't figure how to change that.
What is the issue here? How should I refactor my script so Vue instance's data and Game.population can be linked?
This solution isn't as pretty as throwing your Game object into the Vue instance, but it works.
Forked here: https://codepen.io/andymerskin/pen/LyoEmW
Here's what I changed:
Added a computed getter/setter for population, giving you a clean way to render the array's length in the template, and the ability to assign it in your Game loop using app.population = Game.population in your Game.doPopulatePacks routine.
Changed data.property in the Vue instance to _property to keep the naming conventions paired up for use inside the computed property. It does come with the caveat that it's no longer proxied to avoid conflicts with Vue's internal properties, since we're leaning on the computed property in the template.
(Currently, there isn't a clear convention for defining private properties in a Vue instance, even though underscores _ are the preference in JS more broadly.)
And with that, you'll see in the forked demo that your game's population stays up to date in the UI.
I'm purely curious (high level explanation) how mobx.autorun() is smart enough to grovel through the function it's passed to determine which observables are accessed?
The short story is that autorun does not parse your code looking for usages, but rather records each access of an observable within the first run. This is why it must run once straight away.
It has wrapped the getter and setter of each observable so that it can record which ones you use (via the getter) and build a list of observables that your autorun function used. If any of them change (via the setter), it will detect that and rerun your autorun function.