MST: How to create the main store correctly? - javascript

I want to create rootStore which contains others store. The problem is that the children contain properties like:
id: types.identifier(types.string),
And when I create the rootStore, I get an error from the child:
[mobx-state-tree] Error while converting {} to SomeModelStore: at path "/id" value undefined is not assignable to type: identifier(string) (Value is not a string), expected an instance of identifier(string) or a snapshot like identifier(string) instead.
I tried to use types.late but it did not help.
The solution I found is to wrap all properties into types.maybe
Examples:
error:
https://codesandbox.io/s/yvnznxyvyj?module=%2Fmodels%2FSomeModelStore.js
workaround:
https://codesandbox.io/s/0mv558yq50?module=%2Fmodels%2FSomeModelStore.js

Here https://codesandbox.io/s/yvnznxyvyj?module=%2Fmodels%2FSomeModelStore.js you create an empty object
.model("FirstStore", {
item: types.optional(SomeModelStore, {})
})
but type
SomeModelStore
didn't support empty fields. If you write like this
export const FirstStore = types
.model("FirstStore", {
item: types.optional(SomeModelStore, {
id: 'defaultId',
activate: false,
name: 'defaultName'
})
})
it will work. Or you can use "types.maybe" instead of "types.optional".
export const FirstStore = types
.model("FirstStore", {item: types.maybe(SomeModelStore)})
Also read about types.reference
I think it's a better way to use it in your case.

Related

Prisma/React Query Dependent undefined type challenges

I would like to take the output of one query (a TRPC query on Prisma) and use this as the dependent input in a future query.
I followed the dependent documentation for React Query but running into type errors that the return of the first may possibly be undefined (e.g. product is possibly 'undefined'):
const { data: product } = api.product.getUnique.useQuery({ id: pid });
const options = api.option.getAll.useQuery(
{
product: product.productSize,
region: product.productRegion,
},
{ enabled: !!product }
);
Does the inclusion of enabled not already handle this? If not, what is the correct way to adapt for Typescript.
Just casting the product value as a boolean return any truthy value (f.e if product will be equal to {} it will still result in true, that means that product won't necessarily have the productSize or productRegion properties, I would change it first to:
{ enabled: !!product && product.productSize && product.productRegion }
If that doesn't fix the typescript error, you as a developer can know for sure that the values are actually there so what you can use the as keyword in typescript to tell it that you know for sure that the type is what you want it to be:
(In this example I assumed that the values are string but you can change it to number or whatever the true value of them are)
const options = api.option.getAll.useQuery(
{
product: product.productSize as string,
region: product.productRegion as string,
},
{ enabled: !!product && product.productSize && product.productRegion }
);

Dynamically form Input, to a function in Service, from Input from Template- Angular

Let's say a service has a Subject, whose value you want to dynamically set from a component.
A.service.ts:
//ChartTools: zoom?: boolean, pan?: boolean
public ChartConfig$: BehaviorSubject<ChartTools> = new BehaviorSubject(this.defaultValues);
public changeChartConfig(config: ChartTools):void{
const currentValues = this.ptChartToolsConfig$.value;
this.ptChartToolsConfig$.next({
...currentValues,
pan: config.pan,
zoom: config.zoom
})
}
Now in component A,we want to set this subject dynamically through a function:
A.component.html:
<mat-checkbox
value="zoom"
[checked]= "ToolsConfig$.zoom"
(change) = changeChartConfig($event.value, $event.source._checked)
>
A.component.ts
ngOnInit():void{
private ToolsConfig$ = this.A_Service.ChartConfig$.subscribe();
}
//cTask['constant']= "zoom" | "pan"
changeChartConfig(component: cTask['constant'], checked: boolean):void{
this.A_Service.changeChartConfig({
component : checked
})
}
But, the input 'component' in changeChartConfig() is not even used and I get the error:
Argument of type '{ component: boolean; }' is not assignable to parameter of type 'ChartTools'.
Object literal may only specify known properties, and 'component' does not exist in type 'ChartTools'.
I understand It's saying 'component' doesn't exist in interface ChartTools, but the values of component exist in ChartTools, and I want to use them.
Can someone please help me with what can be done to resolve this?
You have a typescript issue here.
What your error is saying is that you are expecting params to be ChartTools, but you are sending in something that looks different. Check your interface to make sure everything fits in properly. Also if in interface you are lacking component property add it. If everything is fine reset your VScode
So lets say your ChartTools interface looks like this:
interface ChartTools {
property1: string;
property2: number;
component: boolean;
}
The error prevents you to send in a value that lacks property1 and property2, because it is not a ChartTool.
How to fix it:
either make property1 and property2 opitonal:
interface ChartTools {
property1?: string;
property2?: number;
component: boolean;
}
use TS Partials, or add the missing params to you component when you are sending data:
changeChartConfig(component: cTask['constant'], checked: boolean):void{
this.A_Service.changeChartConfig({
component : checked
missingProp1: 'Some value'
missingProp2: 'Another value'
})
}
EDIT
If you are trying to achieve dynamic property name do it this way:
this.A_Service.changeChartConfig({
[component] : checked
// [component] will be parsed instead of being sent in as a string
})

Edit readonly javascript objects

I have a read-only object that is returned by GraphQL (vue-apollo) query, the result which is read-only looks something like this:
result: {
id: 'yh383hjjf',
regulations: [{ title: 'Test', approved: false}]
})
I want to bind this to a form and be able to edit/update the values in the regulations array and save it back to the database.
at the moment when I try to edit I get the error below:
Uncaught TypeError: "title" is read-only
I tried cloning the result returned by the database using object.assign
//target template
const regulatoryApprovals = {
id: null,
regulations: [{ title: null, approved: null}]
})
regulatoryApprovals = Object.assign(regulatoryApprovals, result, {
regulations: Object.assign(regulatoryApprovals.regulations, result.regulations)
})
but this didn't work.
Does anyone know how I can properly clone the result?
regulatoryApprovals= Object.assign(regulatoryApprovals, ... indicates the problem because regulatoryApprovals is modified with Object.assign, so it would need no assignment.
Read-only regulatoryApprovals object needs to be cloned. regulations is an array and won't be merged correctly with Object.assign, unless it's known that array elements need to be replaced. It should be:
regulatoryApprovals = {
...regulatoryApprovals,
...result,
regulations: [...regulatoryApprovals.regulations, result.regulations]
}
Where { ...regulatoryApprovals, ... } is a shortcut for Object.assign({}, regulatoryApprovals, ...).

How do you create edgeless graphql element in Gatsby?

The title may be miss leading but I'm not really sure how do I ask this question correctly. Here is the problem: I'd like to query my own API(not created yet so I made placeholder data) for global settings which might change in the future and I will only need to rebuild the website instead of editing it manually, I want to create source node called CmsSettings and pass it to GraphQL (structure similar to site.siteMetadata) but I don't know how can I achieve that. What I achieved so far is to create a source node called allCmsSettings which has my data as an object in nodes array.
exports.sourceNodes = ({ actions, createNodeId, createContentDigest }) => {
const { createNode } = actions;
const myData = {
key: 123,
app_title: `The foo field of my node`,
...
}
const nodeContent = JSON.stringify(myData);
const nodeMeta = {
id: createNodeId(`my-data${ myData.key }`),
parent: null,
children: [],
internal: {
type: `CmsSettings`,
mediaType: `text/html`,
content: nodeContent,
contentDigest: createContentDigest(myData)
}
}
const node = Object.assign({}, myData, nodeMeta);
createNode(node);
}
Here is the query used to get the data of the source node
allCmsSettings {
edges {
node {
id
app_title
...
}
}
}
Creating a query results in an array of results(which I know is the result of creating source nodes) but I'd like to create that source so that I could query it like this and:
CmsSettings {
app_title
app_keywords
app_descriptions
app_logo_path
brand_name
...
}
You get the point. I was browsing the gatsby node API but I can't find how to achieve this.
Thank you for your help
Nevermind, the answer is pretty simple, if you are new to gatsby just like me the sourceNodes export creates 2 graphql fields for you with all prefix and camel case source node. The thing that I wanted to make is already there and is queryable with
cmsSettings {
app_title
app_keywords
app_descriptions
app_logo_path
brand_name
...
}
Notice the lowercase letter even though it was declared as CmsSettings. It seems that gatsby really does some magic under the hood.

Flow type: either one property is required or the other

I am trying to define a type in flow such that you must specify either a Client or an Invoice. This is my attempt at doing that:
type Client = {
client: {
id: number,
},
invoice?: {
id: number,
},
};
type Invoice = {
client?: {
id: number,
},
invoice: {
id: number,
},
};
type Props = Client | Invoice;
If client is undefined, then invoice must be defined and vice-versa.
However, when I try to access properties based on this, the flow typechecker throws errors:
function getAssignedId({client, invoice}: Props) {
return client ? client.id : invoice.id;
}
results in:
22: return client ? client.id : invoice.id;
^ Cannot get `invoice.id` because property `id` is missing in undefined [1].
References:
21: function getAssignedId({client, invoice}: Asssignement) {
^ [1]
You can try it here.
Any idea why this is happening? Is there another way to achieve this with flow?
When you do deconstruct the type via:
{client, invoice}: Props
the types of those variables are resolved. client and invoice both are resolved as ?{ id: number } since they may or may not exist at the time. The typechecker does not remember that these two objects are linked, because you have separated them.
If you don't separate them, flow can keep track of the union type and do the right thing.
function getAssignedId(obj: Props) {
return obj.client ? obj.client.id : obj.invoice.id;
}
Try flow link
You are not checking if invoice is defined. If you add the check it will not throw the error

Categories