I'm working on a NUXT project and I find myself copying the same actions into multiple store/modules. So I have extracted the actions to a separate file and I now import it into the module. That works great but I occasionally need an additional action that is not boilerplate. How do I import the boilerplate actions and also have a custom action in the module?
Tags module:
import Vue from "vue";
import globalActions from "../utils/Actions.js";
export const state = () => ({ /* removed */ })
export const actions = globalActions;
//Need actions to be a bit more flexible to include non-boilerplate actions
I'm not sure it matters but here is utils/Actions.js It is just the standard "export default {}" that would typically be in the module.
export default {
all({ commit }, all) {
all.data.forEach(item => {
commit("add", item);
});
},
async list({ commit, state, getters, dispatch }) {
/* flush resets the state */
commit("flush");
/*Makes the api call using the repository setup */
let params = getters.params;
const results = await this.$repositories[state.type].index(params);
const ids = results.data.map(item => item.id);
let page = state.page;
dispatch("all", results);
/*Adds a encyclopedia that maps pages to index */
commit("SET_REFERENCE", { page, ids });
commit("totalItems", results.meta.total);
},
}
Ideally, I think the module actions would look something like this:
export const actions = {
list(){ return globalActions.list }
nonBoilerPlateAction({commit})
}
I am pretty sure I will need to change how I import the globalActions and that my "ideal" actions syntax is wrong but I not sure what I need to adjust.
To merge the imported actions and the custom actions, you can merge the two actions in this manner:
export const actions = {
...globalActions,
nonBoilerPlateAction({commit}) {
commit('something')
}
}
Although the way you are using to re-use your module works fine, I'll recommend using namespacing instead. This way all your created module can be easily re-used without having to import them to any other file. You can have access to other modules from another module easily.
Related
Trying out Next.js but I'm struggling with the following. Just tried to install react-hook-mousetrap and imported it like I normally would:
import useMousetrap from "react-hook-mousetrap";
This gives me the following error:
SyntaxError: Cannot use import statement outside a module
1 > module.exports = require("react-hook-mousetrap");
I am not sure what this means? I then thought it might be a problem with Nextjs's SSR, since my library enables hotkeys and will use some browser APIs. If you already know that I am on the wrong track here you can stop reading now.
What I did next however was this, I tried dynamic imports:
1. Dynamic import with next/dynamic
First thing I came across was next/dynamic, but this seems to be for JSX / React Components only (correct me if I'm wrong). Here I will be importing and using a React hook.
2. Dynamic import with await (...).default
So I tried dynamically importing it as a module, but I'm not sure how to do this exactly.
I need to use my hook at the top level of my component, can't make that Component async and now don't know what to do?
const MyComponent = () => {
// (1) TRIED THIS:
const useMousetrap = await import("react-hook-mousetrap").default;
//can't use the hook here since my Component is not async; Can't make the Component async, since this breaks stuff
// (2) TRIED THIS:
(async () => {
const useMousetrap = (await import("react-hook-mousetrap")).default;
// in this async function i can't use the hook, because it needs to be called at the top level.
})()
//....
}
Any advice here would be appreciated!
The error occurs because react-hook-mousetrap is exported as an ESM library. You can have Next.js transpile it using next-transpile-modules in your next.config.js.
const withTM = require('next-transpile-modules')(['react-hook-mousetrap']);
module.exports = withTM({ /* Your Next.js config */});
I don't know if my answer is actual, but i'm facing whith this problem today, and what that i done:
//test component for modal
const Button: React.FC<{close?: () => void}> = ({ close }) => (
<React.Fragment>
<button type="button" onClick={ close }>Close</button>
</React.Fragment>
);
// async call import react custom hook in next js whithout a dynamic
//import
let newHook;
(async () => {
const { hookFromNodeModules } =
await import('path/to/hook');
newHook = hookFromNodeModules;
})();
export default function Home() {
// check if hook is available
const openModal = newHook && newHook();
const { t } = useTranslation('common');
// useCallback for update call function when hook is available
const handleOpen = React.useCallback(() => {
openModal?.openModal(Button, {});
}, [openModal]);
return ( ...your code )
}
hope this help!)
screen from next.js app
In my application, I have a lot of utility functions that do little things from parsing strings to making toasts and so on. My question is how do I access these in other .vue files? I don't want to rewrite these functions for every new Vue component that I write. Will I be able to use these by importing one component into another? (Vue complains if I don't add the component in the template of another, meaning I can't just import that JavaScript). If so, is that a good/sensible thing to do? What's the standard way to do this?
Mixins is a concept you can try.
import the component which you need.
add mixin array as below in your component just above the data section (or wherever possible)
mixins:[yourimportedcomponent],
data:....
Call the method you want using this.theMethodYouWant();
More you can find it here https://v2.vuejs.org/v2/guide/mixins.html
You could create plugin that exposes the functions on Vue. Plugins documentation
// group utils functions together
Vue.prototype.$utils = {
funcA: function () { ... },
funcB: function () { ... }
}
or
Move them all to common utilities module, src/utils.js, then each Vue component can import as needed:
// src/utils.js
const funcA = () => {
console.log('funcA');
}
const funcB = () => {
console.log('funcB');
}
export { funcA, funcB }
// VueComponentA.vue
import { funcA } from 'path/to/utils';
// VueComponentB.vue
import { funcB } from 'path/to/utils';
I am trying to create manual mocks for the #material-ui/core/styles module, which exports the HOC called withStyles.
Using the inline mock with jest.mock works perfectly, but when I try to move this logic into sharable __mocks__ folder, it does not work anymore.
I removed from the file being tested all the unecessary code, just keeping the two lines creating the issues:
import { withStyles } from '#material-ui/core/styles';
console.log('loaded styles:', withStyles);
export default 'hello';
And the test simplified as well, just to see if the mock is working, is the following:
import test from '../test';
console.log(test);
What I have tried is to create a __mocks__ folder in the root of the project, where I have the node_modules folder.
There I created a first folder called #material-ui and inside it another folder called core.
Inside this folder I have a file called styles.js with the following code:
export const withStyles = 'hello world';
So the structure looks like:
- __mocks__
- #material-ui
- core
- styles.js
- node_modules
This is the code which works, with the mock defined inside the same testing file:
jest.mock('#material-ui/core/styles', () => ({
withStyles: () => Component => props => (
<Component
classes=""
{...props}
/>
),
}));
What is happening with the __mocks__ folder is that, if I do not call any
jest.mock('#material-ui/core/styles');
inside my test file, it uses the real withStyles, so the real module.
No mock at all is being used.
If I use the:
jest.mock('#material-ui/core/styles');
It uses an automatic mock autogenerated by jest (or it looks so), skipping the one I define above.
Just a note about packages, I used CRA for bootstrapping the app and the jest version I currently have is the 20, with the react-scripts version 1.0.17
Thanks to you all for you the help!
Here is how you should use manual mocking in __mocks__/#material-ui/core/styles.js:
// Grab the original exports
import * as Styles from '#material-ui/core/styles';
const mockWithStyles = () => {
console.log('withStyles being mocked'); // this shows that it works
/**
* Note: if you want to mock this return value to be
* different within a test suite then use
* the pattern defined here:
* https://jestjs.io/docs/en/manual-mocks
*/
return () => {}; // your mock function (adjust as needed)
// you can also return the same implementation on certain conditions
// return Styles.makeStyles;
};
module.exports = { ...Styles, withStyles: mockWithStyles };
Here is how I manual mock makeStyles:
// Grab the original exports
// eslint-disable-next-line import/no-extraneous-dependencies
import * as Styles from '#material-ui/core/styles';
import createMuiTheme from '#material-ui/core/styles/createMuiTheme';
import options from '../../../src/themes/options';
const mockMakeStyles = func => {
/**
* Note: if you want to mock this return value to be
* different within a test suite then use
* the pattern defined here:
* https://jestjs.io/docs/en/manual-mocks
*/
/**
* Work around because Shallow rendering does not
* Hook context and some other hook features.
* `makeStyles` accept a function as argument (func)
* and that function accept a theme as argument
* so we can take that same function, passing it as
* parameter to the original makeStyles and
* binding it with our custom theme
*/
const theme = createMuiTheme(options);
return Styles.makeStyles(func.bind(null, theme));
};
module.exports = { ...Styles, makeStyles: mockMakeStyles };
And makeStyles result uses React.useContext, so we have to avoid mocking useContext for makeStyles. Either use mockImplementationOnce if you use React.useContext(...) first in you component, or just filter it out in your test code as:
jest.spyOn(React, 'useContext').mockImplementation(context => {
console.log(context);
// only stub the response if
if (context.displayName === 'MyAppContext') {
return {
auth: {},
lang: 'en',
snackbar: () => {},
};
}
const ActualReact = jest.requireActual('react');
return ActualReact.useContext(context);
});
And on your createContext() call, probably in a store.js, add a displayName (standard), or any other custom property to Identify your context:
const store = React.createContext(initialState);
store.displayName = 'MyAppContext';
The makeStyles context displayName will appear as StylesContext and ThemeContext and their implementation will remain untouched to avoid error.
This fixed all kind of mocking problem with makeStyles. But I haven't use withStyles to know its structure deeply, but similar logic should be applicable.
I created the file src/__mocks__/#material-ui/core/styles.jsx in my project and inside I put:
import * as Styles from '#material-ui/core/styles';
import React from 'react';
const withStyles = () => Component => props => <Component classes={{}} {...props} />;
module.exports = { ...Styles, withStyles };
I'm trying to unit test this class that has a dependency of AppDB and createStudy that I need to mock. To get started I'm attempting to unit test the simple method startLoadingData which happens to be a MobX action
import { observable, action } from 'mobx'
import { Intent } from '#blueprintjs/core'
import { createStudy } from '../database/DatabaseInit'
import { AppDB } from '../database/Database'
export default class UIStore {
// ui state
// booleans indicating open/close state of modals
#observable createDialogue
#observable importDialogue
#observable revisionsDialogue
#observable runDialogue
// boolean indicating loading or waiting for async action
#observable loadingData
// array indicating navigation
#observable breadcrumbs
#observable processingMessages
constructor(rootStore) {
this.rootStore = rootStore
this.breadcrumbs = []
this.importDialogue = false
this.createDialogue = false
this.revisionsDialogue = false
this.runDialogue = false
// boolean to display loading blur on table that displays data
this.loadingData = false
// processing messages for import and other async loads
this.processingMessages = []
}
#action startLoadingData() {
this.loadingData = true
}
}
My test file below is getting nowhere because there's an error being thrown related to a separate dependency of sqlite3 in the AppDB and createStudy imports. My understanding is that if I mock those two dependencies that I can avoid the error because they'll be mocked and not real implementations trying to use sqlite3.
// UIStore domain store unit test
// import * as Database from '../../app/database/Database'
// import * as DatabaseInit from '../../app/database/DatabaseInit'
import UIStore from '../../app/stores/UIStore'
describe('UIStore', () => {
beforeEach(() => {
// jest.spyOn(Database, 'AppDB').andReturn('mockAppDB')
// jest.spyOn(DatabaseInit, 'createStudy').andReturn('createStudy')
jest.mock('../../app/database/Database')
// jest.mock('DatabaseInit')
})
it('starts loading data', () => {
const testUIStore = new UIStore(this)
testUIStore.startLoadingData()
expect(testUIStore.loadingData).toBe(true)
})
})
As you can see, trying a bunch of things, but I don't seem to be getting anywhere. I've read about manual mocks, and thought that might be the case so I made a manual mock of Database but not even sure if I'm doing that correctly.
const Database = jest.genMockFromModule('../Database.js')
module.exports = Database
I dont think this matters, but it might be worth noting that AppDB is a ES6 class and createStudy is a method.
Jest should auto mock modules from node_modules if you create a __mocks__ folder in your root project folder and create in that folder mocks for the modules you want auto mocked. By auto mock I mean that when writing a test and Jest detects that folder it will automatically load the mock instead of the original module. This also applies to dependencies of dependencies.
So in your case I would try to create a sqlite3 like so:
/project
|
-> __mocks__
| |
| -> sqlite3/index.js <- export mocked functions
|
-> node_modules
At least this is how I deal with libraries in my Jest tests.
Hope this helps.
I'm displeased with the formulation of the question. Feel encouraged to suggest an improvement. Also, please keep in mind that due to ignoyance (ignorance leading to annoyance), I might have flawed diagnostics of hte issue. Sorry about that.
In this answer it's suggested to use this.$store.xxx and it fails in my code because this is undefined. I strongly suspect something stupid being done by the author of the code (that would be me), so I'll present the schematics of my component layout.
The way it's intended is that I have a landing page index.js that creates two components - one for the visuals of the application and one for the storage of information. The visual App will consist of a navigation bar (and a rendering area later on). The navbar will dispatch commands to the store (and to the viewing area) rendering different *.vue files showing tables, lists etc.
So, how come I get to see the text this is undefined? Is my structure entirely flawed or am I just missing a small detail here and there?
index.js
import Vue from "vue"
import Store from "./vuex_app/store"
import App from "./vuex_modules/app.vue"
new Vue({ el: "#app-base", components: { App }, store: Store });
store.js
import Vue from "vue";
import Vuex from "vuex";
Vue.use(Vuex);
const state = { ... };
const mutations = { ... };
export default new Vuex.Store({ state, mutations });
app.vue
<template><div id="app">App component<navigation></navigation></div></template>
<script>
import navigation from "./navigation.vue"
export default { components: { navigation } }
</script>
navigation.vue
<template><div id="nav-bar"><p v-on:click="updateData">Update</p></div></template>
<script>
import { updateData } from "../vuex_app/actions";
export default {
vuex: {
actions: { updateData },
getters: { ... }
},
methods: {
updateData: () => {
console.log("this is " + this);
this.$store.dispatch("updateData");
}
}
}
</script>
actions.js
export const updateData = ({dispatch}, data) => {
console.log("invoked updateData");
dispatch("UPDATE_DATA", data);
};
Vue.js offers a pretty nice reactive type, minimalist JavaScript framework. Unfortunately, per this link there may be some unusual usage requirements. In this case,
Don’t use arrow functions on an instance property or callback (e.g.
vm.$watch('a', newVal => this.myMethod())). As arrow functions are
bound to the parent context, this will not be the Vue instance as
you’d expect and this.myMethod will be undefined.