I'm trying to create an interpret for Xstate, and I'm trying to pass it a Machine I had created in a separate file, something like this:
import { Machine } from 'xstate';
const testMachine = Machine({
id: 'testMachine',
initial: 'start',
states: {
start: {
on: {
PUB_TOPIC: 'wait_micro_res',
},
},
wait_micro_res: {
on: {
MACHINE_DISCONNECTED: 'disconnection',
CONFIRMATION_RECEIVED: 'wait_order',
},
},
wait_order: {
on: {
DISCONNECTION_ORDER: 'end',
EXPERIMENT_ORDER: 'wait_measurement',
},
},
wait_measurement: {
on: {
EXPERIMENT_FINISHED: 'end',
MEASUREMENT_RECEIVED: 'receive_measurement',
},
},
receive_measurement: {
on: {
SEND_2_EXPERIMENT_MS: 'wait_measurement',
},
},
disconnection: {
on: {
RECONNECTION: 'wait_micro_res',
},
},
end: {
type: 'final',
},
},
});
export default {
testMachine,
};
I'm trying to create it this way:
import { interpret } from 'xstate/lib/interpreter';
import testMachine from '../stateMachine/index';
const machineService = interpret(testMachine)
.onTransition((state) => {
console.log(state.value);
})
.start();
However I'm getting this error:
TypeError: Cannot set property '_sessionid' of undefined
When I try to create the machine in the same file of the interpreter all runs fine. I tried to log the machine and it seems to be imported correctly, but I don't know if there's an additional mistake I'm not aware about
There seems to be a problem with your export. You are exporting { testMachine } as the default export instead of testMachine.
You should use:
export default testMachine;
Then when you import testMachine from '../stateMachine/index'; you will get the desired object.
For now you are importing an object with a property testMachine that contains you machine.
If you want to keep that export, use:
const machineService = interpret(testMachine.testMachine)
Related
So I have a vuejs project and want to use some variable (globally) in any of my components (Variable will not change its value).
I created a variable called STEPS in somefile.js and imported it from components where I want to use.
// somefile.js
export const STEPS = {
PROJECT: 'project',
USER: 'user',
SUMMARY: 'summary',
}
// maybe freeze object here because value will not be changed
// component1.vue
import { STEPS } from 'somefile.js'
export default {
methods: {
someMethod(value) {
if (value === STEPS.PROJECT) {
// do something
}
}
}
}
// component2.vue
import { STEPS } from 'somefile.js'
export default {
methods: {
someMethod(value) {
if (value === STEPS.USER) {
// do something
}
}
}
}
So this actually works? I don't get any errors or anything. But I'm just wondering .. I am not sure if it's okay to use it like this? Just making a .js file and import it anywhere in your code and use it?
So..
I found 2 ways how people use global variables in vuejs.
using instance properties
https://v2.vuejs.org/v2/cookbook/adding-instance-properties.html#Base-Example
Vue.prototype.$STEPS = {
PROJECT: 'project',
USER: 'user',
SUMMARY: 'summary',
}
created() {
console.log(this.$STEPS)
}
Using mixins
https://v2.vuejs.org/v2/guide/mixins.html
var mixin = {
data: function () {
return {
message: 'hello',
foo: 'abc'
}
}
}
new Vue({
mixins: [mixin],
data: function () {
return {
message: 'goodbye',
bar: 'def'
}
},
created: function () {
console.log(this.$data)
// => { message: "goodbye", foo: "abc", bar: "def" }
}
})
So my question is is it okay to use how I used global variable? Just making a variable in javascript file and importing it..
Or should I change it to using instance properties or mixins?
Yes, the way you do it is totally fine. If you want to manage your data centrally where is can be accessed by all components (and can even be changed across all of them simultaneously), you could also have a look at Vuex. Here is also a great (but maybe outdated) tutorial on Vuex.
I am using nuxt and apollo together with: https://github.com/nuxt-community/apollo-module
I have a working GraphQL query (tested in GraphiQL):
(Because I want to fetch the info about my page and also some general SEO information)
{
entries(section: [pages], slug: "my-page-slug") {
slug
title
}
seomatic(uri: "/") {
metaTitleContainer
metaTagContainer
metaLinkContainer
metaScriptContainer
metaJsonLdContainer
}
}
I want to fetch this data as well with apollo in nuxt:
So I tried:
<script>
import page from '~/apollo/queries/page'
import seomatic from '~/apollo/queries/seomatic'
export default {
apollo: {
entries: {
query: page,
prefetch: ({ route }) => ({ slug: route.params.slug }),
variables() {
return { slug: this.$route.params.slug }
}
},
seomatic: {
query: seomatic,
prefetch: true
}
},
…
If I do that I will get an error message:
GraphQL error: Cannot query field "seomatic" on type "Query".
I then found this issue
https://github.com/apollographql/apollo-tooling/issues/648
and I would like to know if ths could be a problem of the apollo nuxt module.
Because following that fix indicated in the issue does not resolve anything.
I further tried to combine the two calls into one:
fragment SeoMaticFragment on Root {
seomatic(uri: "/") {
metaTitleContainer
metaTagContainer
metaLinkContainer
metaScriptContainer
metaJsonLdContainer
}
}
query myQuery($slug: String!) {
entries(section: [pages], slug: $slug) {
slug
title
}
SeoMaticFragment
}
~/apollo/queries/page.gql
But this would first throw an error
fragment Unknown type "Root"
So what is the best way to combine?
Why are the requests failing
is there an option to activate batching like described here: https://blog.apollographql.com/query-batching-in-apollo-63acfd859862
-
const client = new ApolloClient({
// ... other options ...
shouldBatch: true,
});
thank you so much in advance.
Cheers
There is actually a solution to this problem.
I found out that the result hook in vue-apollo solves this problem:
Example code that works:
<script>
import gql from 'graphql-tag'
const query = gql`
{
entries(section: [pages], slug: "my-example-page-slug") {
slug
title
}
seomatic(uri: "/") {
metaTitleContainer
metaTagContainer
metaLinkContainer
metaJsonLdContainer
}
}
`
export default {
data: () => {
return {
page: false,
seomatic: {}
}
},
apollo: {
entries: {
query,
prefetch: ({ route }) => ({ slug: route.params.slug }),
variables() {
return { slug: this.$route.params.slug }
}
},
result(result) {
this.entries = result.data.entries
this.seomatic = result.data.seomatic
}
}
}
</script>
I have a utility file that uses the following implementation with a vuex store:
// example.js
import store from '#/store';
[...]
export default function exampleUtil(value) {
const user = store.state.user.current;
[...]
}
In my test, I found that I can successfully mock the value of user in the following two ways:
Manual mock
// store/__mocks__/index.js
export default {
state: {
user: {
current: {
roles: [],
isAdmin: false,
},
},
},
};
or
Mock function
// example.spec.js
jest.mock('#/store', () => ({
state: {
user: {
current: {
roles: [],
isAdmin: false,
},
},
},
}));
The issue that I'm running into is that I want to be able to change the value of current between tests, such as changing isAdmin to true or updating the array for roles.
What is the best way to do this using Jest mocks?
It turns out that the value of a mock can be changed inside a test directly after importing the mocked file.
Using the mock function example from above:
// example.spec.js
import store from '#/store'; // <-- add this
jest.mock('#/store', () => ({
state: {
user: {
current: {
roles: [],
isAdmin: false,
},
},
},
}));
it('should do things', () => {
store.state.user.current.roles = ['example', 'another']; // <-- change mock value here
[...]
});
I compiled vue-flash-message component from sources and got the following warning:
✘ http://eslint.org/docs/rules/no-param-reassign Assignment to property of function parameter 'Vue'
src\components\vue-flash-message\index.js:173:5
Vue.prototype[options.storage] = FlashBus;
in the following code:
export default {
install(Vue, config = {}) {
const defaults = {
method: 'flash',
storage: '$flashStorage',
createShortcuts: true,
name: 'flash-message',
};
const options = Object.assign(defaults, config);
...
const FlashBus = new Vue({
data() {
return {
storage: {
},
};
},
methods: {
flash(msg, type, opts) {
return new FlashMessage(FlashBus, msg, type, opts);
},
push(id, message) {
Vue.set(this.storage, id, message);
},
destroy(id) {
Vue.delete(this.storage, id);
},
destroyAll() {
Vue.set(this, 'storage', {});
},
},
});
...
Vue.prototype[options.storage] = FlashBus;
...
},
};
is it possible to correct the code and make it compile without warnings?
This is not an issue.
You have an ES Lint rule setup for no-param-reassign. This conflicts with Vue's way of creating plugins, where you are directed to write to the prototype directly. You can see my statement reinforced here
Your only choice is to fork that project, and ignore the line with your linter if it's bugging you that much.
I'm constructing a GraphQL query using vue-apollo and graphql-tag.
If I hardcode the ID I want, it works, but I'd like to pass the current route ID to Vue Apollo as a variable.
Does work (hardcoded ID):
apollo: {
Property: {
query: PropertyQuery,
loadingKey: 'loading',
variables: {
id: 'my-long-id-example'
}
}
}
However, I'm unable to do this:
Doesn't work (trying to access this.$route for the ID):
apollo: {
Property: {
query: PropertyQuery,
loadingKey: 'loading',
variables: {
id: this.$route.params.id
}
}
}
I get the error:
Uncaught TypeError: Cannot read property 'params' of undefined
Is there any way to do this?
EDIT: Full script block to make it easier to see what's going on:
<script>
import gql from 'graphql-tag'
const PropertyQuery = gql`
query Property($id: ID!) {
Property(id: $id) {
id
slug
title
description
price
area
available
image
createdAt
user {
id
firstName
lastName
}
}
}
`
export default {
name: 'Property',
data () {
return {
title: 'Property',
property: {}
}
},
apollo: {
Property: {
query: PropertyQuery,
loadingKey: 'loading',
variables: {
id: this.$route.params.id // Error here!
}
}
}
}
</script>
You can't have access to "this" object like that:
variables: {
id: this.$route.params.id // Error here!
}
But you can like this:
variables () {
return {
id: this.$route.params.id // Works here!
}
}
Readimg the documentation( see Reactive parameters section) of vue-apollo you can use vue reactive properties by using this.propertyName. So just initialize the route params to a data property as then use it in you apollo object like this
export default {
name: 'Property',
data () {
return {
title: 'Property',
property: {},
routeParam: this.$route.params.id
}
},
apollo: {
Property: {
query: PropertyQuery,
loadingKey: 'loading',
// Reactive parameters
variables() {
return{
id: this.routeParam
}
}
}
}
}
While the accepted answer is correct for the poster's example, it's more complex than necessary if you're using simple queries.
In this case, this is not the component instance, so you can't access this.$route
apollo: {
Property: gql`{object(id: ${this.$route.params.id}){prop1, prop2}}`
}
However, you can simply replace it with a function, and it will work as you might expect.
apollo: {
Property () {
return gql`{object(id: ${this.$route.params.id}){prop1, prop2}}`
}
}
No need for setting extra props.