Say I have a module (./my-module.js) that has an object which should be its return value:
let values = { a: 1, b: 2, c: 3 }
// "export values" results in SyntaxError: Unexpected token
So I can import them like:
import {a} from './my-module' // a === 1
import * as myModule from './my-module' // myModule.a === 1
The only way I found is by hard coding the exports:
export let a = values.a
export let b = values.b
export let c = values.c
// or:
export let {a, b, c} = values
Which is not dynamic.
Is it possible to export all values from an object?
I can't really recommend this solution work-around but it does function. Rather than exporting an object, you use named exports each member. In another file, import the first module's named exports into an object and export that object as default. Also export all the named exports from the first module using export * from './file1';
values/value.js
let a = 1;
let b = 2;
let c = 3;
export {a, b, c};
values/index.js
import * as values from './value';
export default values;
export * from './value';
index.js
import values, {a} from './values';
console.log(values, a); // {a: 1, b: 2, c: 3} 1
Does not seem so. Quote from ECMAScript 6 modules: the final syntax:
You may be wondering – why do we need named exports if we could simply default-export objects (like CommonJS)? The answer is that you can’t enforce a static structure via objects and lose all of the associated advantages (described in the next section).
I just had need to do this for a config file.
var config = {
x: "CHANGE_ME",
y: "CHANGE_ME",
z: "CHANGE_ME"
}
export default config;
You can do it like this
import { default as config } from "./config";
console.log(config.x); // CHANGE_ME
This is using Typescript mind you.
try this ugly but workable solution:
// use CommonJS to export all keys
module.exports = { a: 1, b: 2, c: 3 };
// import by key
import { a, b, c } from 'commonjs-style-module';
console.log(a, b, c);
Why not just do a named export of the object:
let values = { a: 1, b: 2, c: 3 }
export { values }
or
export let values = { a: 1, b: 2, c: 3 }
and then a named import where you need it:
import { values } from './my-module'
let foo = values.a
let { a, b, c } = values
or
import { values as myModule } from './my-module'
let foo = myModule.a
let { a, b, c } = myModule
can do default export as well:
let values = { a: 1, b: 2, c: 3 }
export default values
or
export default { a: 1, b: 2, c: 3 }
and then consume it:
import whateverIcallIt from './my-Module'
let foo = whateverIcallIt.a
let {a, b, c } = whateverIcallIt
If you want to export a bunch of individual values, say a bunch of constants, you can:
export const a = 1
export const b = 2
//...
or even
export const a = 1,
b = 2,
c = 3,
//...
and then import them individually:
import { a, b, c } from './my-module'
I suggest the following. Let's expect a module.js:
const values = { a: 1, b: 2, c: 3 };
export { values }; // you could use default, but I'm specific here
and then you can do in an index.js:
import { values } from "module";
// directly access the object
console.log(values.a); // 1
// object destructuring
const { a, b, c } = values;
console.log(a); // 1
console.log(b); // 2
console.log(c); // 3
// selective object destructuring with renaming
const { a:k, c:m } = values;
console.log(k); // 1
console.log(m); // 3
// selective object destructering with renaming and default value
const { a:x, b:y, d:z = 0 } = values;
console.log(x); // 1
console.log(y); // 2
console.log(z); // 0
More examples of destructuring objects: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/Destructuring_assignment#Object_destructuring
export const a = 1;
export const b = 2;
export const c = 3;
This will work w/ Babel transforms today and should take advantage of all the benefits of ES2016 modules whenever that feature actually lands in a browser.
You can also add export default {a, b, c}; which will allow you to import all the values as an object w/o the * as, i.e. import myModule from 'my-module';
Sources:
https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements/export
http://www.2ality.com/2014/09/es6-modules-final.html
You can do lots of stupid thing with javascript. I will leave this quote here from YDKJS book.
Mentioned page of the book ->
https://books.google.com.tr/books?id=iOc6CwAAQBAJ&pg=PT150&lpg=PT150&dq=JS+engine+cannot+statically+analyze+the+contents+of+plain+object&source=bl&ots=7v8fMUgwhx&sig=dP3BpY7mEvpvfyxO_koWaXczBWI&hl=en&sa=X&ved=2ahUKEwi4qseXyrDdAhUS-6QKHZYTAEQQ6AEwAHoECAEQAQ#v=onepage&q=JS%20engine%20cannot%20statically%20analyze%20the%20contents%20of%20plain%20object&f=false
Every answer requires changing of the import statements.
If you want to be able to use:
import {a} from './my-module' // a === 1
import * as myModule from './my-module' // myModule.a === 1
as in the question, and in your my-module you have everything that you need to export in one object (which can be useful e.g. if you want to validate the exported values with Joi or JSON Schema) then your my-module would have to be either:
let values = { a: 1, b: 2, c: 3 }
let {a, b, c} = values;
export {a, b, c};
Or:
let values = { a: 1, b: 2, c: 3 }
export let {a, b, c} = values;
Not pretty, but it compiles to what you need.
See: Babel example
Exporting each variable from your variables file. Then importing them with * as in your other file and exporting the as a constant from that file will give you a dynamic object with the named exports from the first file being attributes on the object exported from the second.
Variables.js
export const var1 = 'first';
export const var2 = 'second':
...
export const varN = 'nth';
Other.js
import * as vars from './Variables';
export const Variables = vars;
Third.js
import { Variables } from './Other';
Variables.var2 === 'second'
Related
I would expect that when calling a getter from the store and assigning it to a variable, any changes to that variable would not result in the store state being updated, as I'm not calling the state directly.
This is true for primitive data, but not for object references.
<script setup>
import { ref } from 'vue'
import { useStudyStore } from '#/study/studyStore'
const StudyStore = useStudyStore()
let string = ref(StudyStore.getString)
string.value = 'bar2' // Does NOT update store
let object = ref(StudyStore.getObject)
object.value.a = 2 // DOES update store
const simpleArray = ref(StudyStore.getSimpleArray)
simpleArray.value[0] = 5 // DOES update store
</script>
// Store
export const useStudyStore = defineStore('study', {
state: () => ({
string: 'bar',
object: { a: 1 },
simpleArray: [1, 2, 3, 4],
}),
getters: {
getString(state) {
return state.string
},
getObject(state) {
return state.object
},
getSimpleArray(state) {
return state.simpleArray
},
},
})
I understand that this is due to how referencing works in Javascript, but I expected Pinia to handle this in some way to prevent accidental updating of the store. Is it really the case that any time we want to protect a non-primitive state value in the store from being mutated we need to parse/stringify them?
const copy = JSON.parse(JSON.stringify(StudyStore.getSimpleArray))
This is caused by the simpleArray being deeply reactive
Generally, you'd expect that everything in store is deeply reactive
If you want to explicitly remove reactivity, use
// the object will be non-reactive, you'll have to `store.simpleArray = store.simpleArray` to update it
simpleArray: markRaw([1, 2, 3, 4])
// the returned value would be non-reactive, but would stealthy change reactive object
getSimpleArray() {
return toRaw(this.simpleArray)
}
// will disallow changes on TS level
getSimpleArray() {
return this.simpleArray as DeepReadonly<typeof this.this.simpleArray>
}
// will disallow changes on proxy level
getSimpleArray() {
return deepReadonly(this.simpleArray)
}
(readonlys may be not is #vue but in #vueuse/core or whatever)
why deeply reactive, say you have deep options
options: Record<someId, { colorOfSomeId: string }>
obviously expected to be deep reactive
In vue2, we can use Object.assign(this.$data, this.$options.data()) to reset all values
But how can we do the same thing (one line coding) in vue3, assuming my setup() is ....
setup(props, context) {
let a = ref('value1')
let b = ref('value2')
let c = ref('value3')
function reset() {
Object.assign(????, ????) or ???? <-- ????
}
}
Object.assign(this.$data, this.$options.data()) is a workaround that relies on options API internals.
It's a good practice to have reusable function for scenarios where initial state needs to be accessed more than once:
const getInitialData = () => ({ a: 1, ... });
It can be called both in data and setup.
Component state or its part can be expressed as single object. In this case it's handled the same way as Vue 2 state:
const formData = reactive(getInitialData());
...
Object.assign(formData, getInitialData());
Otherwise a.value, etc need to be assigned with respective initial values, either manually or with helper function:
let data = getInitialData();
for (let [key, ref] of Object.entries({ a, b, ... }))
ref.value = data[key];
I have a problem with mocking constant in my test. I have a file with app configuration. There are just keys with some values nothing more.
appConfig.js
//imports
...
export const CASHING_AMOUNT_LIMIT = 50;
export const CASHING_DELETE_AMOUNT = 25;
...
and this is the reducer that I want to test:
reducer.js
import {
CASHING_AMOUNT_LIMIT,
CASHING_DELETE_AMOUNT,
} from '../appConfig';
...
export const reducer = handleActions({
[REQUEST_DATA]: (state, action) => {
if (payload.count >= CASHING_AMOUNT_LIMIT) {
// do something with data if the condition is true
}
return {
...someState,
};
},
...
In my test, I want to change a value for CASHING_AMOUNT_LIMIT and check if reducer returns current store. And I don't know how to mock this variable in reducer.js. Here is my test:
...//imports
const mockValues = {
CASHING_AMOUNT_LIMIT: 10,
CASHING_DELETE_AMOUNT: 5,
};
jest.mock('../../appConfig.js', () => mockValues);
const {
CASHING_AMOUNT_LIMIT,
CASHING_DELETE_AMOUNT,
} = require('../../appConfig.js');
...
it('MY awesome test ', () => {
expect(CASHING_AMOUNT_LIMIT).toBe(10);
expect(CASHING_DELETE_AMOUNT).toBe(5);
// HERE is all ok CASHING_AMOUNT_LIMIT = 10 and the second variable is 5
// tests are OK
// ....
expect(storeWithCache.dispatch(requestFunction({ test: 'XX'})))
.toEqual(myStore);
...
In the end I use dispatch which call my reducer action and function in reducer.js it runs OK... but with old value for CASHING_AMOUNT_LIMIT it is still 50 (as in appConfig.js) and I need to set 10
Can somebody help me with mocking CASHING_AMOUNT_LIMIT in reducer.js?
This part
jest.mock('../../appConfig.js', () => mockValues);
Needs to be outside the describe block, under the imports.
I have a solution we have all mock in one file testenv.js and it is importing globally I put jest.mock('../../appConfig.js') there and it works!
What I want to do is save a value before it is changed.
mounted () {
this.noneChangedResults = this.results;
}
So that I can do this.
methods: {
saveChanges(){
this.$router.push({ name: "resultsAfterEdit", params: {newResults: this.results}});
},
exit(){
console.log(this.noneChangedResults);
this.$router.push({ name: "resultsAfterEdit", params: {newResults: this.noneChangedResults}});
}
},
This is done so that I dont save the new results on exit (when exit() is called)
this.results is manipulated with v-model
after this.results is changed so is this.noneChangedResults. I do not want this to happen.
What I believe it's happening is that you're giving a reference of results to noneChangedResults, so when the first one changes the last one changes too because it is a reference to the first object.
To solve this and give noneChangedResults single ownership of it's data just use the Spread Operator.
A possible solution:
mounted () {
this.noneChangedResults = {...this.results};
}
A example look at the console ;D :
// Without spread operator
a = {}
console.log('a', a)
b = {}
console.log('b', b)
b.c = 3
console.log('b', b)
a = b
console.log('a', a)
b.d = 4
console.log('b', b)
console.log('a', a)
// With spread operator
a = {}
console.log('a', a)
b = {}
console.log('b', b)
b.c = 3
console.log('b', b)
a = {...b}
console.log('a', a)
b.d = 4
console.log('b', b)
console.log('a', a)
What is happening here is that the object this.noneChangedResults is just referencing the other object this.results.
A simple fix is:
mounted () {
this.noneChangedResults = JSON.parse(JSON.stringify(this.results));
}
You need to clone this.results in this.noneChangedResults with Object.assign:
this.noneChangedResults = Object.assign({}, this.results)
Simply assigning an object to another will reflect the changes to the first object:
let a = {a: 1}
let b = a
b.a = 2
console.log(a) // Output: {a: 2}
But using Object.assign will make a copy of the first object so the changes to the second object won't affect the first object:
let a = {a: 1}
let b = Object.assign({}, a)
b.a = 2
console.log(a) // Output: {a: 1}
I'm trying to store data for later use in vue component.
Data function, example 1
data:
function () {
return {
username: '',
phoneNumber: '',
}
}
Save method
var x = JSON.stringify(this.$data)
localStorage.setItem('xxx', x);
Load will fail , code:
var x = JSON.parse(localStorage.getItem('xxx'));
this.$data = x; // <<< Not working
When i will change Data function (add container)
data:
function () {
return {
container:{
username: '',
phoneNumber: '',
}
}
}
Load works
var x = JSON.parse(localStorage.getItem('xxx'));
this.$data.container = x.container; // <<< Works
How to not add additional container like in first example
You can't replace $data in the way you're attempting to. Instead, try taking advantage of Object.assign():
var x = JSON.parse(localStorage.getItem('xxx'));
Object.assign(this.$data, x);
This should effectively "merge" the data from x into this.$data, where any properties that match in both objects will have the values in x overwrite the values in this.$data.