How to create types.array in MST from external api - always returning proxy not object - react-native

I'm fetching data from external API, and want to store it in MST store as array. But the result is always proxy, not the object I wanted.
This is result from API:
(4) [Object, Object, Object, Object]
0:Object
id:1
name: "Foobar"
created_at: "2019-04-27 09:09:29"
updated_at:null
deleted_at:null
__proto__:Object
.........
This is my store:
const TypesModel = types.model({
name: types.maybe(types.string),
created_at: types.maybe(types.string)
});
export const TransactionTypeStore = types
.model("TransactionTypeStore", {
transaction_types: types.optional(types.array(TypesModel), [])
})
.actions(self => ({
getTypes: flow(function*(token) {
try {
const res = yield typesApi
.headers({ Authorization: `Bearer ${token}` })
.get()
.json();
console.log("result", res);
self.transaction_types = res;
// res.map(data => {
// self.transaction_types.push(data);
// });
} catch (err) {
console.log(err);
}
})
}));
And this is console.log of my MST store:
transaction_types:Proxy
[[Handler]]:Object
[[Target]]:Array(4)
0:ObjectNode
1:ObjectNode
2:ObjectNode
3:ObjectNode
$treenode:ObjectNode
length:4
toJSON:function toJSON()
Symbol(mobx administration):ObservableArrayAdministration
__proto__:Array(0)
[[IsRevoked]]:false
.........
Does anyone know how to deal with this kind of problem?

its similar to TypeScript, only you are using Mobx .MODEL
So lets say we want to create a ToDo list, which array that has id: number, name: string, isDone: boolean.
You first Define this Interface using Mobx .model, like this:
const singleToDoItem = types.model({
id: types.number,
name: types.string,
isDone: types.boolean
})
we then create an Actual Store, with ARRAY as a type (you can also use .optional) AND then put the singleToDoItem inside the types.array(singleToDoItem), so it looks like this:
const myStore = types.model({
list: types.array(singleToDoItem)
})
FINAL CODE will look like this:
const singleToDoItem = types.model({
id: types.number,
name: types.string,
isDone: types.boolean })
const myStore = types.model({
toDoList: types.array(singleToDoItem) })
I made a video on how to do this on my YouTube channel.

How does your res object look like, it should be an array of { name, created_at } objects - nothing more nothing less, is it? Also transaction_types will never be a mere array - types.array is a complex MST type, it has some array methods, but it's not an array. It's an observable array and you should treat it accordingly.
Also check this video tutorial by Michel Weststrate himself: Use observable objects, arrays, and maps to store state in MobX to get a better grip on the concept (create an account, it's free).

Related

Pinia shared reactivity in stores

Is it possible to share reactive state between stores in Pinia, for example:
export const useMainStore = defineStore('mainStore', {
state: () => ({
data: [{name: "some name", amount: useSomeStore().amount}]
}),
export const useSomeStore = defineStore('someStore', {
state: () => ({
amount: 0
}),
The idea is that useSomeStore.amount value is synced with the useMainStore data value.
So when I change the amount in useSomeStore to 5 I expect that data value in useMainStore will change accordingly to:
[{name: "some name", amount: 5}]
I know that I can subscribe to the store or use watchers but is there a better solution for this?
I made working solution using storeToRefs but not sure if there are drawbacks to this.
https://codesandbox.io/s/competent-breeze-wylkoj?file=/src/stores/tomato.ts
Remember pinia states are reactive objects.
Therefore, you can always set a computed on one of them which references another store's state.
Generic example:
const useStoreOne = defineStore('one', {
state: () => ({
foo: 'bar'
})
})
const useStoreTwo = defineStore('two', {
state: () => ({
foo: computed({
get() { return useStoreOne().foo },
set(val) { useStoreOne().foo = val }
})
})
})
Note: storeToRefs does the same as above. So you can write storeTwo as:
const useStoreTwo = defineStore('two', {
state: () => ({
foo: storeToRefs(useStoreOne()).foo
})
})
But it's kind of pointless. Why would you want to use useStoreTwo().foo anywhere instead of using useStoreOne().foo directly?
Make no mistake: the above pattern sets a two-way binding between the two store's foos. But, the second store's foo is useless. Anywhere you use it, you could be using the first one's foo directly.
On general principles, when you come across this pattern (or across the need for it), it should raise a flag: you're using state management because you want "one source of truth". Not more.

Vuejs Apollo I have a problem to retrieve an id in array

I am using a mutation with apollo and Vuejs to remove an entry for an event from my calendar. I need to retrieve the event id in an array.
deleteEvent: function () {
this.$apollo
.mutate({
mutation: MUTATION_entreePlanningDelete,
variable: {
id: this.id,
},
})
.then((data) => {
console.log(data);
});
},
in this function I appeal to the mutation request.
here is my array (this name : evenements), we can see the id. Can you tell me how to get it back?
try these two options:
for 1st index
console.log(data[0].id);
or if it gives undefined or something else
then try to grab it from object directly
let { id } = data[0];
console.log(id);
also try without the index
I have test this option:
console.log(evenements[0].id);
I get the id , i have test your second option
let { idEvent } = evenements[0].id;
console.log(idEvent);
idEvent is undefined but in this version it's ok
let idEvent = evenements[0].id;
console.log(idEvent);
evenements it's an array but impossible to recover its value elsewhere
I solved my problem
I did not pay attention, we just had to put in the event = parameter to the event selected in the calendar to add .id to say to retrieve the event id.
here is what had to be done on the template side
<v-btn text #click="deleteEvent(selectedEvent)"> Effacer </v-btn>
and here is what had to be done in the delete function
deleteEvent: function (event) {
this.$apollo
.mutate({
mutation: MUTATION_entreePlanningDelete,
variables: {
id: event.id,
},
refetchQueries: [{ query: QUERY_entreesPlanning }]
})
.then((data) => {
console.log(data);
});
},

jsreport-core how add localization?

Consider the following example: https://playground.jsreport.net/w/anon/VkLWfMyMb-7
I was able to recreate this using jsreports-online. How can I add localization to jsreport-core?
app.get('/test', (req, res) => {
jsreport().init().then((reporter: Reporter) => {
const templatePath: string = path.join(__dirname, 'assets', 'template.html');
const template: string = fs.readFileSync(templatePath, 'utf8');
const exampledatapath: string = path.join(__dirname, 'assets', 'exampledata.json');
const exampledata: string = fs.readFileSync(exampledatapath, 'utf8');
let data = JSON.parse(exampledata);
reporter.render({
template: {
content: template,
engine: 'handlebars',
recipe: 'chrome-pdf',
},
data: data
}).then(function (out: any) {
out.stream.pipe(res);
})
.catch(function (e: any) {
res.end(e.message);
});
}).catch(function (e: any) {
res.end(e.message);
});
});
Best I could come up with is something like this data['$localizedResource'] = {key1: 'value1'};
Is there a better or build-in way to do it?
You better handle localization on your own in such a case.
Using the resources extension with jsreport-core would require to install additional extension data. Insert resources to the documents store, reference it in the template call...
I see you are anyway sending the template content manually, not using the store. In this case, you better to store your localized files inside plain json, read them using nodejs and extend the input data with it.
reporter.render({
template: {
content: template,
engine: 'handlebars',
recipe: 'chrome-pdf',
},
data: {
...data,
$localizedResource: JSON.parse(fs.readFileSync('....mylabels-en.json').toString())
}
}

How do I upload files to a graphql backend implemented using graphql-upload using axios?

I have a GraphQL backend implemented using express, express-graphql, graphql and graphql-upload. My GraphQL schema declaration is as follows:
type OProject {
_id: ID!
title: String!
theme: String!
budget: Float!
documentation: String!
date: Date
}
input IProject {
title: String!
theme: String!
budget: Float!
file: Upload!
}
type Mutations {
create(data: IProject): OProject
}
type Mutation {
Project: Mutations
}
I want to make a create request to my GraphQL API at /graphql using axios. How go I go about it?
Following the GraphQL multipart request specification detailed here you would go about doing so by:
creating a FormData instance and populating it with the following:
The operations field,
the map field and,
the files to upload
Creating the FormData Instance
var formData = new FormData();
The operations field:
The value of this field will be a JSON string containing the GraphQL query and variables. You must set all file field in the variables object to null e.g:
const query = `
mutation($project: IProject!) {
Project { create(data: $project) { _id } }
}
`;
const project = {
title: document.getElementById("project-title").value,
theme: document.getElementById("project-theme").value,
budget: Number(document.getElementById("project-budget").value),
file: null
};
const operations = JSON.stringify({ query, variables: { project } });
formData.append("operations", operations);
The map field:
As its name implies, the value of this field will be a JSON string of an object whose keys are the names of the field in the FormData instance containing the files. The value of each field will be an array containing a string indicating to which field in the variables object the file, corresponding to value's key, will be bound to e.g:
const map = {
"0": ["variables.project.file"]
};
formData.append("map", JSON.stringify(map));
The files to upload
You then should add the files to the FormData instance as per the map. In this case;
const file = document.getElementById("file").files[0];
formData.append("0", file);
And that is it. You are now ready to make the request to your backend using axios and the FormData instance:
axios({
url: "/graphql",
method: "post",
data: formData
})
.then(response => { ... })
.catch(error => { ... });
To complete the answer of #Archy
If you are using definition of what you expect in GraphQl like Inputs don't forget to set your graphql mutation definition after the mutation keyword
You have to put the definition of your payload like this
const query = `
mutation MutationName($payload: Input!) {
DocumentUpload(payload: $payload) {
someDataToGet
}
}`
And your operations like this the operationName and the order doesn't matter
const operations = JSON.stringify({ operationName: "MutationName", variables: { payload: { file: null } }, query })

Changing a nested object in Redux using spread operators when the keys are dynamic?

I am trying to store a nested object in redux, but I am using dynamic keys. Here is an example of what it would look like:
// In my Redux Reducer
const initialState = {
stuff: {
<dynamic_key>: { name: 'bob', title: 'mr' },
<dynamic_key>: { name: 'eve', title: 'ms' },
<dynamic_key>: { name: 'car', title: 'na' },
},
};
So I have a redux state called stuff that should hold my nested objects.
However, I cannot correctly save my data. I know react states all have to be immutable, so I am using the spread operator and Object.assign() to create a new object:
const reducer = ( state = initialState, action) => {
// ....
case UPDATE:
return { ...state,
stuff: {
Object.assign({}, action.key, {
name: action.name,
title: action.title
})
}
};
// ....
}
The above is trying to create/update the entire <dynamic_key>, using action.key as the dynamic_key, action.name as the name, and action.title as the title.
An extra tidbit is that if action.key (the dynamic key) doesn't already exist in the redux store stuff, then it should be created rather than error out.
What am I doing wrong? I think I am not using Object.assign() correctly, or not using the spread operator correctly?
EDIT:
Here is my redux action:
export const update = s => ({ type: "UPDATE", payload: {key: s.key, name: s.name, title: s.title} });
Using object spread operator
It seems like in you're case, you've got a couple additionally unnecessary steps. If stuff is supposed to be an object that contains your dynamic keys/value pairs, then you should have:
stuff: Object.assign({}, state.stuff, {[action.key]: {etc...}})
OR
stuff: {
...state.stuff
[action.key]: {name: etc...}
}
Keep in mind that every argument to Object.assign, must be an object. It seems like you are supplying the second argument a string.
I also assume you already have a compiler that allows you to safely use the object spread syntax.
EDIT: added state.stuff to both examples so as not to override previous properties.