So, guys, I am trying to create a dummy database that for a project I am working on, now I will take a Todo app as an example.
const todos = [
{
id: 1,
title: "lunch",
description: "Go for lunc by 2pm"
}
];
export default todos;
this is quite alright! but when I create let's say a Todo list and a list of items to be bought from a store-
const todos = [
{
id: 1,
title: "lunch",
description: "Go for lunc by 2pm"
}
const shoppingList = [
{
id: 1,
item: "lunch",
description: "Go for lunc by 2pm"
}
];
export default todos;
export default shoppingList;
if I try to run this I get this error message
"Only one default export allowed per module. (90:0- which is the last line from what I have given above
)"
module.exports = {
todos: function() {return todos},
shoppingList : function {return shoppingList }
}
You are using two exports with default either use only single export with default passing all components inside like:
export default {todos, shoppingList};
As only one export default is allowed.
Or you can put both components into one root component and export it.
If you still plan to use two different export statements just remove default keyword from it. Hope that helps.
You can also refer here for more detailed understanding:
exporting modules
Related
I'm trying to use an array of dictionaries in python as arguement to a custom dash component and use it as array of objects
in python :
audioList_py = [
{
"name": "random",
"singer": 'waveGAN\'s music',
"cover":
'link_1.jpg',
"musicSrc":
'link_1.mp3',
},
{
"name": "random",
"singer": 'waveGAN\'s music',
"cover":
'link_2.jpg',
"musicSrc":
'link_2.mp3',
},
... etc
]
in Javascript:
audioList1_js = [
{
name: "random",
singer: 'waveGAN\'s music',
cover:'link_1.jpg',
musicSrc: 'link_1.mp3',
},
{
name: "random",
singer: 'waveGAN\'s music',
cover: 'link_2.jpg',
musicSrc: 'link_2.mp3',
},
... etc
]
Here is snippet of javascript code of the dash custom component:
export default class MusicComponent extends Component {
render() {
const {id, audioLists} = this.props;
return (
<div>
<h1>{id}</h1>
<ReactJkMusicPlayer audioLists={audio_list}/>,
</div>
);
}
}
MusicComponent.defaultProps = {};
MusicComponent.propTypes = {
/**
* The ID used to identify this component in Dash callbacks.
*/
audios: PropTypes.array,
id: PropTypes.string,
};
And using the generated component in python:
app = dash.Dash(__name__)
app.layout = html.Div([
music_component.MusicComponent(audios=audioList_py),
html.Div(id='output'),
... etc
])
But I got :
TypeError: The `music_component.MusicComponent` component (version 0.0.1) received an unexpected keyword argument: `audios`Allowed arguments: id
What I am doing wrong ?
Any help or advice will be appreciated, Thanks a lot.
Make sure you run npm run build after you make a change to your custom React component. With those proptypes you shouldn't get that error. If I remove the audios proptype I can reproduce that error.
Besides that you pass a value to the audios property:
music_component.MusicComponent(audios=audioList_py)
but you try to retrieve audioLists from props:
const {id, audioLists} = this.props;
Change this to:
const {id, audios} = this.props;
Demo
export default class MusicComponent extends Component {
render() {
const {id, audios} = this.props;
return (
<div>
<h1>{id}</h1>
<ReactJkMusicPlayer audioLists={audios} />
</div>
);
}
}
MusicComponent.defaultProps = {};
MusicComponent.propTypes = {
/**
* The ID used to identify this component in Dash callbacks.
*/
id: PropTypes.string,
audios: PropTypes.array,
};
Issue fixed, I should run : npm run build:backends to generate the Python, R and Julia class files for the components, but instead I was executing npm run build:js and this command just generate the JavaScript bundle (which didn't know about the new props).
And set the audios property in the component to be like so:
MusicComponent.defaultProps = {audios: audioList1};
MusicComponent.propTypes = {
id: PropTypes.string,
audios: PropTypes.arrayOf(PropTypes.objectOf(PropTypes.string)).isRequired
};
So, the following implementation works just fine to read JSON data and turn it into rendered components - until I try to add the children. Then, it spits out an error.
function:
const catalogRenderer = (config) => {
if (typeof KeysToComponentMap[config.component] !== "undefined") {
return React.createElement(
KeysToComponentMap[config.component],
{
key: config.key,
title: config.title
},
{
config.children && config.children.map(c => catalogRenderer(c))
}
);
}
}
error:
app.js:134 Uncaught Error: Module build failed (from ./node_modules/babel-loader/lib/index.js)
"...Scripts/CatalogRenderer.js: Unexpected token, expected "," (25:14)"
console:
},
24 | {
> 25 | config.children && config.children.map(c => catalogRenderer(c))
| ^
26 | }
27 | );
28 | }
I'm using react as part of an electron application, it's a long story about all the moving parts, but everything else so far has worked just fine. In the editor, if I move to the preceding { from that mysteriously disliked . on line 25, it's highlighting the period as if this should somehow close the bracket.
Is there something I'm not understanding about the syntax here? The same thing happens if I attempt to just map and render the children like so:
{
config.children.map(c => catalogRenderer(c))
}
I've tried enclosing the whole statement in brackets, curly braces, parentheses--no matter what I do, babel seems to expect a comma, but giving it a comma obviously doesn't help. Any idea what I'm doing wrong?
eta: This is the JSON object I'm attempting to render from:
const catConfig = {
catalog: [
{
component: 'pen',
title: `B.C. Palmer`,
key: `B.C.PalmerPen`,
children: `A child string`
},
{
component: 'content',
key: `B.C.PalmerWorldList`,
children: [
{
component: 'world',
title: `Rismere`,
key: `RismereWorld`
},
{
component: 'content',
key: `RismereSeries`,
children: [
{
component: 'series',
title: `The Eidolon War`,
key: `TheEidolonWarSeries`
},
{
component: 'content',
key: `TheEidolonWarBooks`,
children: [
{
component: 'book',
title: `Magic's Heart`,
key: `MagicsHeartBook`
},
{
component: 'book',
title: `Magic's Fury`,
key: `MagicsFuryBook`
},
{
component: 'book',
title: `Magic's Grace`,
key: `MagicsGraceBook`
}
]
}
]
}
]
},
{
component: 'pen',
title: `Simon Strange`,
key: `SimonStrangePen`
}
]
}
This JSON will be generated via a database call, and written each time the database is updated, and update the state of the 'catalog' component.
So, for example, the second object in the catalog array above is a container which, when the first 'pen' component is clicked, becomes visible and shows a list of 'world' components (in this case, just the one.) However, the function only successfully renders any 'parent' components--if I take out the curly braces at lines 24 and 26, it simply skips them but doesn't error.
The components are composed of button elements and a div (content). The buttons will likely become Link element when I get this working, but the original version was written in vanilla javascript, I haven't implemented routing with the catalog yet. So, the pen component for example:
import React from 'react'
export default penButton => {
return(
<button className="catalogItem pen">
<img src="src/icons/catPenName.png" className="catalogIcon"/>
<p className="contentLabel">{penButton.title}</p>
</button>
)
}
Is a top level component, and gets rendered just fine. It's next sibling (and the next sibling of any button except a book) is content:
import React from 'react'
export default contentList => {
return(
<div className="contentList">
</div>
)
}
contentList is just a div with the contentList class, which handles visibility and animation. Should I have a place for the "children" key in JSON to populate the children of content?
When you want to render multipile children elemen'ts into your react component, you need to pass each child as a seperate parameter.
See this answer as an example:
how to render multiple children without JSX
so your solution should be to use spread syntax.
here is an example:
const catalogRenderer = (config) => {
if (typeof KeysToComponentMap[config.component] !== "undefined") {
let childs = [];
if (config.children) {
childs = config.children.map(c => catalogRenderer(c));
}
return React.createElement(
KeysToComponentMap[config.component],
{
key: config.key,
title: config.title
},
...childs
);
}
}
Well, that was simple and a little silly. I updated the content component to:
import React from 'react'
export default contentList => {
return(
<div className="contentList">
{contentList.children} <---- added
</div>
)
}
Content didn't have a place to put children. Obviously.
I've been working for the past two days on a task which is adding the capability to translate a whole website from English to Spanish when the user selects the toggle button, However, I'm really new into Redux (use it once on a completely different project). The people who gave me the code already configured the reducers, I just need to read the status on each component.
I've tried using this code on one of the components:
const store = createStore(reducer);
store.dispatch({
type: 'TOGGLE-LANG'
});
store.subscribe(() => console.log(store.getState()));
However, it is still has something missing, and at this point, I'm completely lost and I would love to have some guidance to know what to do.
I created a Gist with all the code involved in this task, It has commented on what's the expected behavior
[Gist Link] : https://gist.github.com/ManudeQuevedo/12cd63cf7431b5ec9b982a37391b7c56
Currently, there are no errors, it is recognizing the reducer (Lang), but I would love to know how to make it actionable in the other components that need to be translated. Thanks in advance!
You can use i18n. Replacing the text contents on your website to keys, and adding a map matching the keys to different languages.
For example, before you have
<div>
name:
</div>
Now it will be like:
<div>
{t('name')}
</div>
Here is the link of an example project
At the end this is what I did:
lang.js (reducer)
import { fromJS } from 'immutable';
const initState = fromJS({
value: 'en',
translations: {
en: {
component: {
title: 'Mobile Connectivity',
subtitle: 'Smart Messaging'
}
},
es: {
component: {
title: 'Conectividad Móvil',
subtitle: 'Mensajería Inteligente'
}
},
}
});
export default (state = initState, action) => {
switch (action.type) {
case "CHANGE_LANGUAGE":
return {
...state,
value: action.payload
};
default:
return state;
}
};
and I implemented it by destructuring it on this component like so:
[Gist][1]
I added it to a gist because it's a long code so it would be more readable like that.
I am refactoring some code in my app and turns out,the below logic it is repeated in many many components.
import component1 from '...'
import component2 from '...'
import component3 from '...'
//...many others
export default {
//other data
components: {
component1,
component2,
component3
//...
}
}
Does exists a shorter approach in order to clean my code?
Thanks for your time
Below are 3 ways.I prefer method 3 by the way.
Method 1
Create a js file in my case dynamic_imports.js:
export default function (config) {
let registered_components = {}
for (let component of config.components) {
registered_components[component.name] = () => System.import(`../${config.path}/${component.file_name}.vue`)
}
return registered_components
}
In the component in which you have many component imports and registrations
import dynamic_import from '#/services/dynamic_imports' //importing the above file
let components = dynamic_import({
path: 'components/servers',
components: [
{ name: 'server-one', file_name: 'serverOne' },
{ name: 'server-two', file_name: 'serverTwo' },
]
})
export default {
//...other code
components: components
}
As a result you will import and register your components with "clean code".
But note that this worked for me,maybe it has to modified a lit bit to fit your needs,to understand:
The property path means that will look at this path for the names specified in file_name.The name property is the name you register the component
Method 2
If you don't like the above look below to another way:
function import_component(cmp_name){
return System.import(`#/components/${cmp_name}.vue`);
}
export default{
components: {
'component1': () => import_component('componentOne'),
'component2': () => import_component('componentTwo'),
'component3': () => import_component('componentThree')
}
}
Method 3
If again you are saying: This is not a cleaner way,take a look below but keep in mind that if you are working in team and skills differ,then some programmers will be a little bit confused.
dynamic_imports.js
export default function ({path, file_names, component_names}) {
let registered_components = {}
for (let [index, file_name] of file_names.entries()) {
registered_components[component_names[index]] = () => System.import(`../${path}/${file_name}.vue`)
}
return registered_components
}
In your component
import dynamic_import from '#/services/dynamic_imports'
let components = dynamic_import({
path: 'components/servers',
file_names: ['serverOne', 'serverTwo'],
component_names: ['server-one', 'server-two']
})
export default {
components: components
}
You can automatically register such repeated base components globally using the pattern described in the official docs
https://v2.vuejs.org/v2/guide/components-registration.html#Automatic-Global-Registration-of-Base-Components
Chris Fritz also talks about this pattern in his awesome video where he mentions 7 secret patterns for cleaner code and productivity boost while working with Vue.js
The disadvantage of this approach, however, is that the components that you autoregister this way always end up in the main bundle and therefore cannot be lazy loaded/code-splitted. So make sure you do this only for the base components that are very generic.
This was the question got me stuck for a little bit. Unfortunately, I coudn't find answer here (asking also didn't help). So after doing some research and asking here and there, it seems that I got the solution to this issue.
If you have a question that you already know the answer to, and you
would like to document that knowledge in public so that others
(including yourself) can find it later.
Of course, my answer may not be the ideal one, moreover I know it is not, that's the key point why I'm posting - to improve it.
Note, I'm not using actions in example. The idea is the same.
Let's begin with stating the problem:
Imagine we have App.vue which dynamically generates its local component named Hello.
<template>
<div id="app">
<div>
<hello v-for="i in jobs" :key="i" :id="i"></hello>
<button #click="addJob">New</button>
</div>
</div>
</template>
<script>
import Hello from './components/Hello'
export default {
components: {
Hello
}...
store.js
export const store = new Vuex.Store({
state: {
jobs: []
}
})
We are using v-for directive to generate components by iterating through an array jobs. Our store as of now consists of only state with an empty array.
Button New should do 2 things:
1) create new component Hello, in other words add element to jobs (let it be numbers), which are going to be assigned as key and id of <hello>, and passed to local component as props.
2) generate local stores - modules - to keep any data scoped to newly created components.
Hello.vue
<template>
<div>
<input type="number" :value="count">
<button #click="updateCountPlus">+1</button>
</div>
</template>
export default {
props: ['id']
}
Simple component - input with a button adding 1.
Our goal is to design something like this:
For the first operation of NEW button - generating components - we add mutation to our store.js
mutations: {
addJob (state) {
state.jobs.push(state.jobs.length + 1)
...
}
Second, creating local modules. Here we're going to use reusableModule to generated multiple instances of a module. That module we keep in separate file for convinience. Also, note use of function for declaring module state.
const state = () => {
return {
count: 0
}
}
const getters = {
count: (state) => state.count
}
const mutations = {
updateCountPlus (state) {
state.count++
}
}
export default {
state,
getters,
mutations
}
To use reusableModule we import it and apply dynamic module registration.
store.js
import module from './reusableModule'
const {state: stateModule, getters, mutations} = module
export const store = new Vuex.Store({
state: {
jobs: []
},
mutations: {
addJob (state) {
state.jobs.push(state.jobs.length + 1)
store.registerModule(`module${state.jobs.length}`, {
state: stateModule,
getters,
mutations,
namespaced: true // making our module reusable
})
}
}
})
After, we're going to link Hello.vue with its storage. We may need state, getters, mutations, actions from vuex. To access storage we need to create our getters. Same with mutations.
Home.vue
<script>
export default {
props: ['id'],
computed: {
count () {
return this.$store.getters[`module${this.id}/count`]
}
},
methods: {
updateCountPlus () {
this.$store.commit(`module${this.id}/updateCountPlus`)
}
}
}
</script>
Imagine we have lots of getters, mutations and actions. Why not use {mapGetters} or {mapMutations}? When we have several modules and we know the path to module needed, we can do it. Unfortunately, we do not have access to module name.
The code is run when the component's module is executed (when your app
is booting), not when the component is created. So these helpers can
only be used if you know the module name ahead of time.
There is little help here. We can separate our getters and mutations and then import them as an object and keep it clean.
<script>
import computed from '../store/moduleGetters'
import methods from '../store/moduleMutations'
export default {
props: ['id'],
computed,
methods
}
</script>
Returning to App component. We have to commit our mutation and also let's create some getter for App. To show how can we access data located into modules.
store.js
export const store = new Vuex.Store({
state: {
jobs: []
},
getters: {
jobs: state => state.jobs,
sumAll (state, getters) {
let s = 0
for (let i = 1; i <= state.jobs.length; i++) {
s += getters[`module${i}/count`]
}
return s
}
}
...
Finishing code in App component
<script>
import Hello from './components/Hello'
import {mapMutations, mapGetters} from 'vuex'
export default {
components: {
Hello
},
computed: {
...mapGetters([
'jobs',
'sumAll'
])
},
methods: {
...mapMutations([
'addJob'
])
}
}
</script>
Hi and thank you for posting your question and your solution.
I started learning Vuex couple days ago and came across a similar problem. I've checked your solution and came up with mine which doesn't require registering new modules. I find it to be quite an overkill and to be honest I don't understand why you do it. There is always a possibility I've misunderstood the problem.
I've created a copy of your markup with a few differences for clarity and demonstration purposes.
I've got:
JobList.vue - main custom component
Job.vue - job-list child custom component
jobs.js - vuex store module file
JobList.vue (which is responsible for wrapping the job(s) list items)
<template>
<div>
<job v-for="(job, index) in jobs" :data="job" :key="job.id"></job>
<h3>Create New Job</h3>
<form #submit.prevent="addJob">
<input type="text" v-model="newJobName" required>
<button type="submit">Add Job</button>
</form>
</div>
</template>
<script>
import store from '../store/index'
import job from './job';
export default {
components: { job },
data() {
return {
newJobName: ''
};
},
computed: {
jobs() {
return store.state.jobs.jobs;
}
},
methods: {
addJob() {
store.dispatch('newJob', this.newJobName);
}
}
}
</script>
The Job
<template>
<div>
<h5>Id: {{ data.id }}</h5>
<h4>{{ data.name }}</h4>
<p>{{ data.active}}</p>
<button type="button" #click="toggleJobState">Toggle</button>
<hr>
</div>
</template>
<script>
import store from '../store/index'
export default {
props: ['data'],
methods: {
toggleJobState() {
store.dispatch('toggleJobState', this.data.id);
}
}
}
</script>
And finally the jobs.js Vuex module file:
export default {
state: {
jobs: [
{
id: 1,
name: 'light',
active: false
},
{
id: 2,
name: 'medium',
active: false
},
{
id: 3,
name: 'heavy',
active: false
}
]
},
actions: { //methods
newJob(context, jobName) {
context.state.jobs.push({
id: context.getters.newJobId,
name: jobName,
active: false
});
},
toggleJobState(context, id) {
context.state.jobs.forEach((job) => {
if(job.id === id) { job.active = !job.active; }
})
}
},
getters: { //computed properties
newJobId(state) { return state.jobs.length + 1; }
}
}
It's possible to add new jobs to the store and as the "active" property suggest, you can control every single individual job without the need for a new custom vuex module.