I am trying to dispatch my Vuex application actions from dev tools console.
Vue application starts using VuexPersistence. Note that application is being exposed by doing window.app = app;.
import VuexPersistence from 'vuex-persist';
const app = new VuexPersistence({
key: 'vuex',
modules: ['auth', 'todo'],
});
export default ({ store }) => {
app.plugin(store);
};
window.app = app;
Code for the action which I want to dispatch is displayed below (working well)
export default {
async actionOne({ commit, rootGetters }, formData) {
try {
let response;
if (rootGetters['auth/isAuthenticated']) {
response = await this.$formNewTodo(formData);
} else {
response = await this.$formNewTodoGuest(formData);
}
const form = {
id: response.id,
description: response.description,
cost: response.cost,
};
commit('SET_NEW_TODO_FORM', form);
commit('SET_NEW_TODO_STEP', STEP_TWO);
} catch (error) {
commit('SET_NEW_TODO_ERROR_DATA', error?.response);
commit('SET_NEW_TODO_ERROR', true);
}
}
})
What I would like to achieve is to dispatch SET_NEW_TODO_FORM, and SET_NEW_TODO_STEP actions from the console.
So, I would like to run something like app.$store.dispatch('SET_NEW_TODO_FORM', payload)
When, I inspect app from console, this is what I get
VuexPersistence {_mutex: SimplePromiseQueue, key: 'vuex', subscribed: true, supportCircular: false, subscriber: ƒ, …}
RESTORE_MUTATION: ƒ RESTORE_MUTATION(state, savedState)
asyncStorage: false
filter: (mutation) => true
key: "vuex"
mergeOption: "replaceArrays"
plugin: (store) => {…}
reducer: (state) => {…}
restoreState: (key, storage) => {…}
saveState: (key, state, storage) => {…}
storage: Storage {vuex: '{"auth":{"accessToken":null,"refreshToken":null,"f…courierName":""},"currentStep":1,"promoCode":""}}', ga:user: '0.5943659671156558.0.9182858112406254', length: 2}
strictMode: false
subscribed: true
subscriber: (store) => (handler) => store.subscribe(handler)
supportCircular: false
_mutex: SimplePromiseQueue {_queue: Array(0), _flushing: false}
[[Prototype]]: Object
Related
I'm using vue2 with composition Api, vuex and apollo client to request a graphql API and I have problems when mocking composable functions with jest
// store-service.ts
export function apolloQueryService(): {
// do some graphql stuff
return { result, loading, error };
}
// store-module.ts
import { apolloQueryService } from 'store-service'
export StoreModule {
state: ()=> ({
result: {}
}),
actions: {
fetchData({commit}) {
const { result, loading, error } = apolloQueryService()
commit('setState', result);
}
},
mutations: {
setState(state, result): {
state.result = result
}
}
}
The Test:
// store-module.spec.ts
import { StoreModule } from store-module.ts
const store = StoreModule
describe('store-module.ts', () => {
beforeEach(() => {
jest.mock('store-service', () => ({
apolloQueryService: jest.fn().mockReturnValue({
result: { value: 'foo' }, loading: false, error: {}
})
}))
})
test('action', async ()=> {
const commit = jest.fn();
await store.actions.fetchData({ commit });
expect(commit).toHaveBeenCalledWith('setData', { value: 'foo' });
})
}
The test fails, because the commit gets called with ('setData', { value: undefined }) which is the result from the original apolloQueryService. My Mock doesn't seem to work. Am I doing something wrong? Appreciate any help, thanks!
Try this :
// store-module.spec.ts
import { StoreModule } from store-module.ts
// first mock the module. use the absolute path to store-service.ts from the project root
jest.mock('store-service');
// then you import the mocked module.
import { apolloQueryService } from 'store-service';
// finally, you add the mock return values for the mock module
apolloQueryService.mockReturnValue({
result: { value: 'foo' }, loading: false, error: {}
});
/* if the import order above creates a problem for you,
you can extract the first step (jest.mock) to an external setup file.
You should do this if you are supposed to mock it in all tests anyway.
https://jestjs.io/docs/configuration#setupfiles-array */
const store = StoreModule
describe('store-module.ts', () => {
test('action', async ()=> {
const commit = jest.fn();
await store.actions.fetchData({ commit });
expect(commit).toHaveBeenCalledWith('setData', { value: 'foo' });
})
}
How can I access a mutation from one file in my store folder that exists in another file in my store folder?
Here's my directory:
store/
user.js
loading.js
In user.js I have:
async googleSignInRedirect({ commit }) {
try {
const result = await this.$fire.auth.getRedirectResult()
if (result.credential) {
// const credential = result.credential
// console.log('got a credential? ', credential)
this.$router.replace('/')
commit('loading/SET_LOADING', false) //< -- what is correct way to write this ?
}
return null
} catch (error) {
console.error(error)
}
},
Here is the loading.js code:
export const state = () => ({
loading: false
})
export const mutations = {
SET_LOADING(state, payload) {
state.loading = payload
}
}
How can I access loading.js from user.js ? If I do the above style, I get the following error in console:
vuex.esm.js?2f62:791 [vuex] unknown local mutation type: loading/SET_LOADING, global type: user/loading/SET_LOADING
Just pass { root: true } as the final argument to commit, so:
async googleSignInRedirect({ commit }) {
try {
const result = await this.$fire.auth.getRedirectResult()
if (result.credential) {
this.$router.replace('/')
commit('loading/SET_LOADING', false, { root: true })
}
return null
} catch (error) {
console.error(error)
}
}
You can find more details in the Vuex documentation.
I have been trying to solve this problem for a few hours now to no avail. Could someone help me spot the problem?
The error I am getting is:
Error: [vuex] Do not mutate vuex store state outside mutation handlers
Here is my login script section with the offending function in login()
<script>
import { auth, firestoreDB } from "#/firebase/init.js";
export default {
name: "login",
props: {
source: String
},
////////
layout: "login",
data() {
return {
show1: false,
password: "",
rules: {
required: value => !!value || "Required.",
min: v => v.length >= 8 || "Min 8 characters",
emailMatch: () => "The email and password you entered don't match"
},
email: null,
feedback: null
};
},
methods: {
login() {
if (this.email && this.password) {
auth
.signInWithEmailAndPassword(this.email, this.password)
.then(cred => {
//this.$router.push("/");
this.$store.dispatch("user/login", cred);
console.log()
this.$router.push("/forms")
console.log("DONE")
})
.catch(err => {
this.feedback = err.message;
});
} else {
this.feedback = "Please fill in both fields";
}
},
signup() {
this.$router.push("signup");
}
}
};
</script>
import { auth, firestoreDB } from "#/firebase/init.js";
export const state = () => ({
profile: null,
credentials: null,
userID: null
})
export const getters = {
getinfo:(state) =>{
return state.credentials
},
isAuthenticated:(state)=>{
if (state.credentials != null) {
return true
} else {
return false
}
}
}
export const mutations = {
commitCredentials(state, credentials) {
state.credentials = credentials
},
commitProfile(state, profile) {
state.profile = profile
},
logout(state){
state.credentials = null,
state.profile = null
}
}
export const actions = {
login({commit},credentials) {
return firestoreDB.collection("Users").where('email', '==', auth.currentUser.email).get()
.then(snapshot => {
snapshot.forEach(doc => {
let profile = {...doc.data()}
commit("commitCredentials", credentials)
commit("commitProfile", profile)
})
}).catch((e) => {
console.log(e)
})
},
credentials({ commit }, credentials) {
commit("commitCredentials",credentials)
},
logout() {
commit("logout")
},
}
I have checked that there is no where else that is directly calling the store state.
I have worked out that if I don't do the commitCredentials mutation which mutates credentials, the problem doesn't happen.
Another note to add, the error keeps printing to console as if it were on a for loop. So my console is flooded with this same message.
I am pretty sure this is to do with the firebase auth sign in and how the Credential object is being changed by it without me knowing, but I can't seem to narrow it down.
Any help would be very much welcomed.
Found the answer.
https://firebase.nuxtjs.org/guide/options/#auth
signInWithEmailAndPassword(this.email, this.password)
.then(cred)
"Do not save authUser directly to the store, since this will save an object reference to the state which gets directly updated by Firebase Auth periodically and therefore throws a vuex error if strict != false."
Credential object is constantly being changed by the firebase library and passing the credential object is just passing a reference not the actual values itself.
The solution is to just save the values within the object.
I have no idea why I'm getting this error. there's no file referenced no line referenced. I only know the action thats executed which results in the error.
The full error:
console.error "redux-persist/createPersistoid: error serializing
state", TypeError: JSON.stringify cannot serialize cyclic structures.
Here is the action that causes the error:
export const loadInitialDiscover = () => {
return (dispatch) => {
dispatch({ type: types.DISCOVER_LOADING });
return getNewest(dispatch);
};
};
const getNewest = (dispatch) => {
return firebase
.firestore()
.collection('users')
.where('role', '==', 'a')
.where('active', '==', true)
.orderBy('createdAt')
.limit(10)
.get()
.then((querySnapshot) => {
const newest = [];
querySnapshot.forEach((queryDocSnapshot) => {
const profile = queryDocSnapshot.data();
newest.push(profile);
});
dispatch({ type: types.LOAD_NEWEST, payload: newest });
})
.catch((err) => loadFail(dispatch, err.message));
};
implementation of action in screen:
componentDidMount () {
//console.log('INITIAL REDUX STATE: ' + JSON.stringify(this.props.newest));
this.props.newest == null ? this.props.loadInitialDiscover() : null;
}
notice this .stringify() is commented out
Ive removed literally any and every existing JSON.stringify, deleted build folder, restarted bundler, restarted simulator. the error persists.
here is relevant reducer:
const INITIAL_STATE = {
newest: null,
loading: false,
error: ''
};
export default (state = INITIAL_STATE, action) => {
switch (action.type) {
case types.DISCOVER_LOADING:
return {
...state,
loading: true,
error: ''
};
case types.LOAD_FAIL:
return {
...state,
loading: false,
error: action.info
};
case types.LOAD_NEWEST:
return {
...state,
loading: false,
error: '',
newest: action.payload
};
}
}
and here's the persist config:
const persistAuthConfig = {
key: 'auth',
storage: AsyncStorage
};
const persistUserConfig = {
key: 'user',
storage: AsyncStorage
};
const persistDiscoverConfig = {
key: 'discover',
storage: AsyncStorage
};
const AppReducer = combineReducers({
auth: persistReducer(persistAuthConfig, AuthReducer),
user: persistReducer(persistUserConfig, UserReducer),
discover: persistReducer(persistDiscoverConfig, DiscoverReducer)
});
export default AppReducer;
How do I get this error gone?
The Firestore query response contains cyclic data (ref parameter) that is not possible to serialised by JSON.stringify hence the error from Redux-Persist, (yes; redux-persist use it under the hood). Just remove the ref parameter containing the cyclic data from all the objects inserted into the array newest array.
Also, do a check on other parameters that cannot be serialised by JSON.stringify like ref.
The below may help you to achieve this:-
const newest = []; querySnapshot.forEach((queryDocSnapshot) => {
const profile = queryDocSnapshot.data();
const {ref, ...profileSerialisable} = profile;
newest.push(profileSerialisable);
});
Please refer here for more discussions based on this.
I'm using Express Graphql server with react native and Relay. My device does connects to the subscription but it does not subscribe to it. Here's my index.js on the server
const subscriptionServer = SubscriptionServer.create(
{
execute,
subscribe,
schema,
onOperation: (message, params, webSocket) => {
console.log(params)
return params;
},
onConnect: () => {
// My device does connects
console.log("client connected")
}
},
{
server,
path: '/subscriptions'
},
);
app.use('/graphql', graphqlHTTP({
schema,
graphiql: true
}));
app.use('/graphiql', graphiqlExpress({
endpointURL: '/graphql',
subscriptionsEndpoint: `ws://127.0.0.1:8080/subscriptions`
}));
server.listen(PORT, ()=> {
console.log("Groceries running on port " + PORT)
console.log(
`subscriptions is now running on ws://localhost:${PORT}/subscriptions'}`
);
});
The resolver for subscription on the server, it was quite troublesome to figure out since everyone is using executable schema from apolloGraphql.
export default {
type: OrderEdges,
args: {
ShopId: {type: GraphQLID},
},
subscribe: withFilter(() => pubsub.asyncIterator('orderConfirmed'), (payload, variables) => {
console.log(payload)
console.log(variables)
return payload.orderConfirmed.node.ShopId == variables.ShopId;
}),
}
Now the react-native client. My subscription setup with relay environment.
const setupSubscriptions = (config, variables, cacheConfig, observer) => {
const query = config.text; //does console logs the query
const subscriptionClient = new SubscriptionClient(`ws://192.168.0.100:8080/subscriptions`, {reconnect:true});
subscriptionClient.request({query, variables}, (err, result) => {
console.log(err) // doesn't get call inside the request method
observer.onNext(data:result)
})
}
My subscription method,
export default function() {
const variables = {
ShopId: shop.getShop()[0].id
}
requestSubscription(
environment,
{
subscription,
variables,
onCompleted: (res, err) => {
console.log(res)
console.log(err)
},
updater: (store) => {...},
onError: error => console.error(error),
onNext: (response) => {console.log(response)}
});
}
the component where I'm calling to subscribe,
import subscription from '../../GraphQLQueries/subscriptions/orderConfirmed';
class OrdersBox extends React.Component {
constructor(props) {
super(props);
}
componentDidMount() {
//initializing subscription
orderSubscriptions();
}
When the device starts the app, my device is connected to the web socket as I can see the console.log statement inside the onConnect method in SubscriptionServer. But when the payload is published after a mutation, the subscribe method doesn't get called. I can't seem to figure out what I'm doing wrong. Maybe it's some react-native specific config that I'm missing cuz everything seems to work fine when I test it on graphiql.
I can't find any example of react-native and relay subscriptions used with express graphql.
note: Everything is working when I use subscription with graphiql. But not with react-native and relay.
Thanks in advance guys
....
I wasn't returning the subscriptionClient.request method. Adding a return statement solved the problem. You don't have to return when using subscribe method in subscriptions-transport-ws#0.8.3. But version 0.9.1 replaces the subscribe function with request which does require it to return.
try:
function setupSubscription(config, variables, cacheConfig, observer) {
const query = config.text;
const subscriptionClient = new SubscriptionClient(websocketURL, {
reconnect: true
});
const client = subscriptionClient.request({ query, variables }).subscribe({
next: result => {
observer.onNext({ data: result.data });
},
complete: () => {
observer.onCompleted();
},
error: error => {
observer.onError(error);
}
});
return {
dispose: client.unsubscribe
};
}
subscriptions-transport-ws#0.9.1