Catching errors in computed properties in Vue - vue.js

In VueJS I have a vuex store getter which may throw an error of type ErrorOne or ErrorTwo.
// myGetter.js
export class ErrorOne extends Error {}
export class ErrorTwo extends Error {}
export default () => {
if (...) {
throw new ErrorOne()
}
if (...) {
throw new ErrorTwo()
}
return ...
}
And in a computed property in my Vue component I am using this getter but I want to handle these errors in a try catch block.
// MyComponent.vue
import { ErrorOne, ErrorTwo } from '.../myGetter'
export default {
...,
data() {
return {
errorOne: false,
errorTwo: false
}
},
computed: {
myGetterComputed() {
try {
const value = this.$store.getters['.../myGetter']
this.errorOne = false // Unexpected side effect
this.errorTwo = false // Unexpected side effect
return value
} catch (err) {
switch (err.constructor) {
case ErrorOne:
this.errorOne = true // Unexpected side effect
break
case ErrorTwo:
this.errorTwo = true // Unexpected side effect
break
}
}
}
}
}
But eslint tells me [eslint] Unexpected side effect in "myComputedGetter" computed property. [vue/no-side-effects-in-computed-properties].
What is the correct way to handle errors in Vue computed properties in my use case?
Should I move myGetterComputed to data and use a watch method to handle updates?

If I am going straight to answer your question I can tell you that eslint is using this rule to warn you about an unintended side effect. As per the eslint-plugin-vue docs
It is considered a very bad practice to introduce side effects inside computed properties. It makes the code not predictable and hard to understand.
Basically what we need to remember is that computed properties are meant to be used when there's heavy logic associated to how we treat data inside templates. So you shouldn't be updating any properties/data inside a computed property logic.
I would like to help you further if you provide a more detailed example of what you're trying yo achieve

Related

How to mock a named import object type const in react native using jest?

Hii everyone currently I'm using the following library
https://www.npmjs.com/package/react-native-biometrics
for my project but I stuck here when I'm trying to write the unit tests for it I have mocked the library like below
import ReactNativeBiometrics, { BiometryTypes } from 'react-native-biometrics';
jest.mock ('react-native-biometrics', () =>
{
class ReactNativeBiometrics {
constructor(){
}
isSensorAvailable() {
}
}
return ReactNativeBiometrics;
});
Now I'm trying to mock
BiometryTypes
but its not getting mocked anyone can give some idea how this can be mocked.
BiometryTypes is exported like below
export declare const BiometryTypes: {
TouchID: string;
FaceID: string;
Biometrics: string;
};
I tried like below but its not working
import { BiometryTypes } from 'react-native-biometrics';
import * as all from 'react-native-biometrics';
const TouchID = 'TouchID' ;
all.BiometryTypes = {
TouchID: TouchID,
FaceID: TouchID
};
You can combine named and default mocks in factory function:
jest.mock ('react-native-biometrics', () =>
{
class ReactNativeBiometrics {
......
}
return {
__esModule: true
default: ReactNativeBiometrics,
BiometryTypes: { TouchID: 'mock1', FaceID: 'mock2', .... } // static values
};
});
Jest docs clarify why this is necessary:
When using the factory parameter for an ES6 module with a default export, the __esModule: true property needs to be specified. This property is normally generated by Babel / TypeScript, but here it needs to be set manually. When importing a default export, it's an instruction to import the property named default from the export object
Beware! With this approach, object BiometryTypes will be mocked once and forever, while you probably want to test different variants of it. Maybe doMock and unmock could help to overcome this, but in my opinion much much more reliable is to create several .test.js files, each one would mock BiometryTypes in unique way(e.g. FaceID_and_TouchID.test.js, TouchID-only.test.js etc). jest.mock is quite magical because it's hoisted to the very top, using unmock/doMock may break if values have already caught by other modules or closures in the same module.

mobx computed always update that computed doesn't work

import {makeAutoObservable} from "mobx";
class Test {
id = 0
constructor() {
makeAutoObservable(this)
}
get total() {
console.log('enss')
return 2;
}
}
const store = new Test();
export default store;
call:
import {isComputed, isComputedProp} from "mobx";
console.log(isComputedProp(Test, 'total'),Test.total,Test.total, Test.total, isComputedProp(Test, 'total'))
console output:
enss
enss
enss
true 2 2 2 true
the computed did not work and does not serve as a cache.
i using mobx 6.6 version in react 18.
Thank you very much for your answer!
This is explained in the docs here
It sometimes confuses people new to MobX, perhaps used to a library like Reselect, that if you create a computed property but don't use it anywhere in a reaction, it is not memoized and appears to be recomputed more often than necessary. For example, if we extended the above example with calling console.log(order.total) twice, after we called stop(), the value would be recomputed twice.
Basically it won't be cached if you use it outside of reactive context.
You can use computed({ keepAlive: true }) option to change this behaviour

trace() error "can only be used inside a tracked computed value or a Reaction" when used inside computed

I've used MobX for a few years now, and love it, but sometimes my trace calls are not functioning, and I don't understand why not. There must be some fundamental thing that I've completely misunderstood, but most likely have been lucky enough to get through anyway. Here's an example of using trace() where I'm getting an error:
import { computed, observable, trace } from "mobx";
class Stat {
#observable baseValue = 1;
#computed get value() {
trace();
return this.baseValue;
}
}
const strength = new Stat();
strength.baseValue = strength.baseValue + 1;
The expected output, in my mind, is that trace reacts to the change in "baseValue" and logs the change. Instead, I'm getting the following error:
Error: [MobX] 'trace(break?)' can only be used inside a tracked computed value or a Reaction. Consider passing in the computed value or reaction explicitly
"Inside a tracked computed value" is, to my understanding, exactly what I'm doing. Or..?
Full sandbox: https://codesandbox.io/s/mobx-trace-trouble-ki2qj?file=/index.ts:0-312
As far as I understand this phrase
inside a tracked computed value or a Reaction.
you need to access computed value inside reactive context, like inside observer or reaction or autorun. Otherwise trace just don't have information about what is going on because your computed value is untracked at that moment by any observer.
So this will work:
const MyComponent = observer(() => {
return <div>{strength.value}</name>
})
or this
autorun(() => {
console.log(strength.value);
});

Stencil: Sudden build error without message after adding `#Method` to component

I cannot give too much information here (because there really isn't), but only this:
All of the sudden, after adding a #Method function to a stencil component:
#Method()
async setMenuItems(items: Element[]): Promise<void> {
// code here
}
the component stopped compiling with the following - really unhelpful - error:
[ ERROR ] ./src/components/menu-content/menu-content.tsx:63:44
build error
L62: #Method()
L63: async setMenuItems(elements: Element[]): Promise<void> {
L64: const unsupportedChildren = elements.filter(e => !this.isSupportedChild(e)).map(e => e.tagName);
[12:37.1] build failed in 7.02 s
Things to notice
the return type Promise<void> inside the error-message is highlighted red
there are other #Methods that do work within this component (even with the same return type).
the "broken" #Method is structurally equal to those that do work.
TypeScript compiler does not complain about anything
Only stencil compiler fails
I already tried...
to google for this issue - did not find any hints to this problem
to remove the async and add return Promise.resolve()
to rename the method (I mean.. why not)
to move the method to another place in class
to remove the whole method (compiles fine x( )
to remove the #Method decorator (compiled, but of course my method is removed from API)
to delete node_modules folder and reinstall
I remember that I already had this error once, and apparently I somehow fixed it (or not, idk). But if I did, I cannot remember how.
Does anyone have an idea how to debug this - or even better fix?
I figured it out. A more complete version of my component is:
import { Element, ... } from '#stencil/core';
class MenuContent {
#Element() element: MenuContentElement;
#Method()
setMenuItems(elements: Element[]): Promise<void> {
// ------------------^^^^^^^
// Element is meant to be the built-in browser interface for Element
// but stencil thinks that this is the imported Element from '#stencil/core'!
}
}
The exact problem here is, that the stencil-compiler seems to assume that the elements parameter is of type Element that is imported from #stencil/core which is obviously wrong and leads to this strange unhelpful error.
Possible Solutions
1. Use an alias type for the built-in Element type
// types.ts
export type DomElement = Element;
// menu-content.tsx
import { DomElement } from './types';
...
async setMenuItems(elements: DomElement[]): Promise<void> { ... }
2. Switch to HTMLElement
Note: This is only legit, when you don't need to support other Element-types such as SVGElements for example!
async setMenuItems(elements: HTMLElement[]): Promise<void> { ... }
3. Use alias in import statement
Please note: When using #stencil eslint rules, they will complain about your renamed import and say that "own class members are not allowed to be public".
I created a ticket for it here: https://github.com/ionic-team/stencil-eslint/issues/28
import { Element as ElementDecorator} from '#stencil/core';
class MenuContent {
// eslint will complain about that:
// "Own class properties cannot be public."
#ElementDecorator() element: HTMLMenuContentElement;
}
I experienced this same issue not with the Element type but with the Event type, so it appears to be rooted deeper - also about a year after you reported this issue it seems to still be a problem with Stencil

react native immutable data structure error

In my application, I have the following code.
case ADD_MOVEMENT:
let am_selectedTrainerMovements = state.selectedTrainerMovements;
state.trainerMovements.forEach(movement => {
if (movement.id === action.movement.id) {
am_selectedTrainerMovements.push(movement);
}
});
return {
...state,
selectedTrainerMovements: am_selectedTrainerMovements
};
break;
In the reducer I use this case. But this gives me an error saying "the push method cannot be invoked on immutable data structure"
What am I doing wrong here?
You set am_selectedTrainerMovements equal to state.selectedTrainerMovements and so you are just creating a new pointer to the exact same object. So when you call am_selectedTrainerMovements.push to are trying to change the state which is immutable.
You you need to clone state.selectedTrainerMovements, for instance:
let am_selectedTrainerMovements =
Object.assign({}, state. state.selectedTrainerMovements);