Add reaction() utility#61
Conversation
| try { | ||
| effect(value, previousValue); | ||
| } catch (e) { | ||
| // TODO: we actually want this to be unhandled, but Vitest complains. |
There was a problem hiding this comment.
how does vitest complain? we can use expect / assert throws:
or for async:
- https://vitest.dev/api/expect.html#rejects
- (they don't have an
assertapi for this one)
There was a problem hiding this comment.
This isn't about asserting that something throws, because the reaction() call isn't on the stack anymore by the time the test effect throws, and doesn't return a promise or something that can contain an async error. This is about user code that throws, but since it's called by a notify callback, letting that be uncaught so that uncaught error handling could handle it.
Without the catch(), Vitest produces an error like:
⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯ Unhandled Errors ⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯
Vitest caught 1 unhandled error during the test run.
This might cause false positive tests. Resolve unhandled errors to make sure your tests are not affected.
⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯ Unhandled Rejection ⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯
Error: Oops
❯ effect tests/subtle/reaction.test.ts:124:15
122| if (value === 1) {
123| thrown = true;
124| throw new Error("Oops");
| ^
125| }
126| thrown = false;
❯ notify src/subtle/reaction.ts:34:9
This error originated in "tests/subtle/reaction.test.ts" test file. It doesn't mean the error was thrown inside the file itself, but while it was running.
⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯
Ideally, we exclude just this test from Vitest's unhandled error check. If another test, say a users' own tests, generated an unhandled error, we would want Vitest to fail.
I tried adding an uncaughException handler, but that didn't prevent Vitest's from running.
There was a problem hiding this comment.
hmm -- ok, thanks for explaining! I'll take a poke after a readme entry is added -- I want to understand this more 🤔
There was a problem hiding this comment.
I figured something out for handling this, and providing the erroring behavior to users -- lemme know what you think
NullVoxPopuli
left a comment
There was a problem hiding this comment.
Lookin good so far! just left a couple notes / comments / suggestions! 🎉
|
hello @justinfagnani
p.s. usually I imagined reaction as an effect with untrack function reaction(getter, reactionFn) {
effect(() => {
const value = getter();
untrack(() => reactionFn(value));
});
}but with hope it is possible for more optimized way (without auto-collect dependency on each call), so watcher is the way to allow that |
I guess?
await 0;Awaiting anything enqueues a microtask. This is similar to |
| // => 1 logs | ||
| ``` | ||
|
|
||
| #### Reactions |
This adds a
reaction()utility that's similar to MobX's reaction() as mentioned in #15 (comment).reaction()takes two arguments, a data function and an effect function. The data function is wrapped in a Computed, and when the return value of the data function changes, the effect function is called with the current and previous values. An optional equality function is accepted.The effect function is only called the first time the data function return value changes. It is not called with the initial value.
Reactions can be unsubscribed to by invoking the cleanup function that it returns.