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';
Related
For example, the recommended way of importing in React Bootstrap is to go this way:
import Button from 'react-bootstrap/Button' instead of import { Button } from 'react-bootstrap';
The reason is "Doing so pulls in only the specific components that you use, which can significantly reduce the amount of code you end up sending to the client."
source: https://react-bootstrap.github.io/getting-started/introduction/
Same for React MUI components:
import Button from '#mui/material/Button';
source: https://mui.com/material-ui/getting-started/usage/
I want to implement something similar in my React components library, to limit the usage of code in the bundle, but I don't know how they implement this specific pattern. I have looked at their code base, but I don't quite understand.
Basically it is all about modules and module files and their organization. You can have a lot of.. lets call them folders, "compoments/*" for example. "components/button", "components/alert", "component/badge", and other things. All of them will have some index.js or .ts file that will export or declare and export all the functionality that needed in order to make this component work, 'react-bootstrap/Button' for example. Ideally all those subfolders or submodules are independend from each other, no references between them but probably each one will have 1 reference to 1 common/shared submodule like "components/common" which will contain some constants, for example, and no references to other files. At the top level of them you will have another index.js or .ts file that is referencing all of those components, so "components/index.js" will import and reexport all the nested components index files. So in order to import a Button, for example, you can either import "components/index.js" file with all the other imports this file is using, either only 1 single "components/button/index.js" file which is obviously much more easy to fetch. Just imagine a tree data structure, you import root of the tree (root index.js) - you get all the tree nodes. You import one specific Node (components/button/index.js) of the tree - just load all the childs (imports) of that node.
Sorry for a long read but asuming you mentioned webpack - there is a technique called tree-shaking which will cut off all the unused things.
Info about modules: https://www.w3schools.com/js/js_modules.asp
Info about Tree-Shaking: https://webpack.js.org/guides/tree-shaking/
It might not be as complicated as you think. Let's say you write the following library:
// your-library.js
const A = 22
const B = 33
export function getA () { return A }
export function getB () { return B }
export function APlusB () { return A + B }
// a lot of other stuff here
If some consumer of your library wants to make use of the APlusB function, they must do the following:
// their-website.js
import { APlusB } from 'your-library'
const C = APlusB()
However, depending on how the code is bundled, they may or may not wind up with the entire your-library file in their web bundle. Modern bundling tools like Webpack may provide tree shaking to eliminate dead code, but this should be considered an additional optimization that the API consumer can opt into rather than a core behavior of the import spec.
To make your library more flexible, you can split up independent functions or chunks of functionality into their own files while still providing a full bundle for users who prefer that option. For example:
// your-library/constants.js
export const A = 22
export const B = 33
// your-library/aplusb.js
import { A, B } from 'constants'
export default function APlusB () { return A + B }
// your-library/index.js
// instead of declaring everything in one file, export it from each module
export * from 'constants'
export { default as APlusB } from 'aplusb'
// more exports here
For distribution purposes you can package your library like so:
your-library
|__aplusb.js
|__constants.js
|__index.js
You mentioned react-bootstrap and you can see this exact pattern in their file structure:
https://github.com/react-bootstrap/react-bootstrap/tree/master/src
and you can see they aggregate and re-export modules in their index file here:
https://github.com/react-bootstrap/react-bootstrap/blob/master/src/index.tsx
Essentially, what you are asking is:
"How to export react components"
OR
"How are react components exported to be able to use it in a different react project ?"
Now coming to your actual question:
import Button from 'react-bootstrap/Button' instead of import { Button } from 'react-bootstrap';
The reason is 'Button' component is the default export of that file react-bootstrap/Button.tsx. So there is no need for destructuring a specific component.
If you export multiple components/ functions out of a file, only 1 of them can be a default export.
If you have only 1 export in a file you can make it the default export.
Consider the file project/elements.js
export default function Button(){
// Implementation of custom button component
}
export function Link(){
// Implementation of custom Link component
}
function Image(){
// Implementation of custom Image component
}
Notice that the Button component has 'default' as a keyword and the Link component doesn't.
The Image component can't even be imported and can only be used by other functions/components in the same file.
Now in project/index.js
import 'Button', {Link} from './elements.js'
As Button component is the default export its possible to import without destructuring and as Link component is a regular export, I have to destructure it for importing.
I have a class file, utility.js for all the common functions.
However some of the functions require certain library import
e.g: NetInfo library
utility.js
import NetInfo from "#react-native-community/netinfo";
export async function networkConnection() {
const state = await NetInfo.fetch();
return state.type;
}
However, in order for utility.js to be reusable in another project, another project has to have NetInfo installed. Is it a good practice if instead of importing modules directly, I import only when I need to use it, and pass the import object along to the function?
utility.js
export async function networkConnection({ NetInfo }) {
const state = await NetInfo.fetch();
return state.type;
}
and when using it
app.js
import NetInfo from "#react-native-community/netinfo";
import { networkConnection } from "./utility.js"
const network = networkConnection({ NetInfo })
This way, I can copy and paste utility.js to any project, without the need to install all the import packages. Also, this pattern seems to be able to resolve common Circular Dependencies error by limiting the import statement.
Is this the best practice for creating a common, reusable function file?
Your question illustrates the difference between a closure and a plain function, however, neither are pure.
If the function only serves to await a promise and return a property value, it doesn't really make sense to use the non-closure approach... unless you've created a module (utility.js in your example) with other exports unrelated to the NetInfo context. If that's the case, I think that's the issue to focus on: reorganization of your code's concerns.
How many characters it takes to get the value two ways:
console.log(`await networkConnection()`.length); // 25
console.log(`(await NetInfo.fetch()).type`.length); // 28
Is it worth the abstraction? Up to your brain and your fingers.
I initialized i18n translation object once in a component (a first component that loads in the app ). That same object is required In all other components. I don't want to re-initialize it in every component. What's the way around? Making it available to window scope doesn't help as I need to use it in the render() method.
Please suggest a generic solution for these problems and not i18n specific solution.
Beyond React
You might not be aware that an import is global already. If you export an object (singleton) it is then globally accessible as an import statement and it can also be modified globally.
If you want to initialize something globally but ensure its only modified once, you can use this singleton approach that initially has modifiable properties but then you can use Object.freeze after its first use to ensure its immutable in your init scenario.
const myInitObject = {}
export default myInitObject
then in your init method referencing it:
import myInitObject from './myInitObject'
myInitObject.someProp = 'i am about to get cold'
Object.freeze(myInitObject)
The myInitObject will still be global as it can be referenced anywhere as an import but will remain frozen and throw if anyone attempts to modify it.
Example of react state using singleton
https://codesandbox.io/s/adoring-architecture-ru3vt
(see UserContext.tsx)
If using react-create-app
(what I was looking for actually) In this scenario you can also initialize global objects cleanly when referencing environment variables.
Creating a .env file at the root of your project with prefixed REACT_APP_ variables inside does quite nicely. You can reference within your JS and JSX process.env.REACT_APP_SOME_VAR as you need AND it's immutable by design.
This avoids having to set window.myVar = %REACT_APP_MY_VAR% in HTML.
See more useful details about this from Facebook directly:
https://facebook.github.io/create-react-app/docs/adding-custom-environment-variables
Why don't you try using Context?
You can declare a global context variable in any of the parent components and this variable will be accessible across the component tree by this.context.varname. You only have to specify childContextTypes and getChildContext in the parent component and thereafter you can use/modify this from any component by just specifying contextTypes in the child component.
However, please take a note of this as mentioned in docs:
Just as global variables are best avoided when writing clear code, you should avoid using context in most cases. In particular, think twice before using it to "save typing" and using it instead of passing explicit props.
Create a file named "config.js" in ./src folder with this content:
module.exports = global.config = {
i18n: {
welcome: {
en: "Welcome",
fa: "خوش آمدید"
}
// rest of your translation object
}
// other global config variables you wish
};
In your main file "index.js" put this line:
import './config';
Everywhere you need your object use this:
global.config.i18n.welcome.en
Is not recommended but.... you can use componentWillMount from your app class to add your global variables trough it... a bit like so:
componentWillMount: function () {
window.MyVars = {
ajax: require('../helpers/ajax.jsx'),
utils: require('../helpers/utils.jsx')
};
}
still consider this a hack... but it will get your job done
btw componentWillMount executes once before rendering, see more here:
https://reactjs.org/docs/react-component.html#mounting-componentwillmount
Here is a modern approach, using globalThis, we took for our React Native app.
globalThis is now included in...
Modern browsers - MDN documentation
Typescript 3.4 - Handbook documentation
ESLint v7 - Release notes
appGlobals.ts
// define our parent property accessible via globalThis. Also apply the TypeScript type.
var app: globalAppVariables;
// define the child properties and their types.
type globalAppVariables = {
messageLimit: number;
// more can go here.
};
// set the values.
globalThis.app = {
messageLimit: 10,
// more can go here.
};
// Freeze so these can only be defined in this file.
Object.freeze(globalThis.app);
App.tsx (our main entry point file)
import './appGlobals'
// other code
anyWhereElseInTheApp.tsx
const chatGroupQuery = useQuery(GET_CHAT_GROUP_WITH_MESSAGES_BY_ID, {
variables: {
chatGroupId,
currentUserId: me.id,
messageLimit: globalThis.app.messageLimit, // 👈 used here.
},
});
Can keep global variables in webpack i.e. in webpack.config.js
externals: {
'config': JSON.stringify({ GLOBAL_VARIABLE: "global var value" })
}
In js module can read like
var config = require('config')
var GLOBAL_VARIABLE = config.GLOBAL_VARIABLE
Hope this will help.
The best way I have found so far is to use React Context but to isolate it inside a high order provider component.
Maybe it's using a sledge-hammer to crack a nut, but using environment variables (with Dotenv https://www.npmjs.com/package/dotenv) you can also provide values throughout your React app. And that without any overhead code where they are used.
I came here because I found that some of the variables defined in my env files where static throughout the different envs, so I searched for a way to move them out of the env files. But honestly I don't like any of the alternatives I found here. I don't want to set up and use a context everytime I need those values.
I am not experienced when it comes to environments, so please, if there is a downside to this approach, let me know.
Create a file :
import React from "react";
const AppContext = {};
export default AppContext;
then in App.js, update the value
import AppContext from './AppContext';
AppContext.username = uname.value;
Now if you want the username to be used in another screen:
import AppContext from './AppContext';
AppContext.username to be used for accessing it.
For only declaring something, try this. Make sure MyObj is assigned at the proper time for you want to access it in render(), many ways was published before this thread. Maybe one of the simplest ways if undefined then create it does the job.
declare global {
interface Window {
MyObj: any;
}
}
USE CUSTOM HOOKS
It is very simple if you use custom hooks
Refer this link
https://stackoverflow.com/a/73678597/19969598
Full sample usage is available in the above post
This answer is for global part of question not I18N.
I wanted a global variable and function across all components of my application and without child-parent relationship.
This Answer is like a good one; but it was not completely clear to me so i had to test it my way.
I used below approach; not sure if this is a "good or bad practice" or even "off-topic"; but share in case help someone.
Global.jsx
const Global = () => { }
export default Global;
Global.var = 100;
Global.func = () => {
Global.var += 1;
alert(Global.var);
}
MyComponent1.jsx
import Global from "./Global";
import React from "react";
const MyComponent1 = () => {
return ( <h1 onClick={Global.func}>COM1: {Global.var}</h1>)
}
export default MyComponent1;
MyComponent2.jsx
import Global from "./Global";
import React from "react";
const MyComponent2 = () => {
return ( <h1 onClick={Global.func}>COM2: {Global.var}</h1>)
}
export default MyComponent2;
And anywhere like index.js
root.render(
<div>
.
.
.
<MyComponent1/>
<MyComponent1/>
<MyComponent2/>
<MyComponent2/>
.
.
.
</div>
);
Note: This way you have access to a global function or variable; but provided sample cannot update (render) screen itself cause no state or prop has been changed.
We can change the solution like this and keep ref of our components or DOM objects in our Global Zone like this (Not that i do not know its a good practice or even the worst case; so its on your own):
Global.jsx
const Global = () => { }
export default Global;
Global.var = 100;
Global.refs = [];
Global.inc = () => {
Global.var += 1;
Global.refs.forEach(ref => {
ref.current.innerText = Global.var;
});
}
MyComponent1.jsx, MyComponent2.jsx, ...
import Global from "./Global";
import React, { createRef } from "react";
const MyComponent1 = () => {
const ref = createRef();
Global.refs.push(ref);
return (<div onClick={Global.inc}>
<h2>COM1:</h2>
<h3 ref={ref} >{Global.var}</h3>
</div>);
};
export default MyComponent1;
I don't know what they're trying to say with this "React Context" stuff - they're talking Greek, to me, but here's how I did it:
Carrying values between functions, on the same page
In your constructor, bind your setter:
this.setSomeVariable = this.setSomeVariable.bind(this);
Then declare a function just below your constructor:
setSomeVariable(propertyTextToAdd) {
this.setState({
myProperty: propertyTextToAdd
});
}
When you want to set it, call this.setSomeVariable("some value");
(You might even be able to get away with this.state.myProperty = "some value";)
When you want to get it, call var myProp = this.state.myProperty;
Using alert(myProp); should give you some value .
Extra scaffolding method to carry values across pages/components
You can assign a model to this (technically this.stores), so you can then reference it with this.state:
import Reflux from 'reflux'
import Actions from '~/actions/actions'
class YourForm extends Reflux.Store
{
constructor()
{
super();
this.state = {
someGlobalVariable: '',
};
this.listenables = Actions;
this.baseState = {
someGlobalVariable: '',
};
}
onUpdateFields(name, value) {
this.setState({
[name]: value,
});
}
onResetFields() {
this.setState({
someGlobalVariable: '',
});
}
}
const reqformdata = new YourForm
export default reqformdata
Save this to a folder called stores as yourForm.jsx.
Then you can do this in another page:
import React from 'react'
import Reflux from 'reflux'
import {Form} from 'reactstrap'
import YourForm from '~/stores/yourForm.jsx'
Reflux.defineReact(React)
class SomePage extends Reflux.Component {
constructor(props) {
super(props);
this.state = {
someLocalVariable: '',
}
this.stores = [
YourForm,
]
}
render() {
const myVar = this.state.someGlobalVariable;
return (
<Form>
<div>{myVar}</div>
</Form>
)
}
}
export default SomePage
If you had set this.state.someGlobalVariable in another component using a function like:
setSomeVariable(propertyTextToAdd) {
this.setState({
myGlobalVariable: propertyTextToAdd
});
}
that you bind in the constructor with:
this.setSomeVariable = this.setSomeVariable.bind(this);
the value in propertyTextToAdd would be displayed in SomePage using the code shown above.
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.
Actually i want to know where to store common components methods in #NUXT.JS.
things which i have tried.
--> Storing common code in middleware (its use-less) because according to my knowledge middleware is only capable of handling request and response to server.
methods: {
// states methods.
SwitchManager: function (__dataContainer, __key, __value) {
// stand alone debugger for this module.
let debug = __debug('computed:_3levelSwitch')
// debug showing function loaded.
debug('(h1:sizeCheck) creating switch..')
// switch.
switch (__key) {
// fake allow set value to true of given key
default:
this[__dataContainer][__key][__value] = true
break
}
return this[__dataContainer][__key][__value]
},
SizeCheck: function (__size) {
// stand alone debugger for this module.
let debug = __debug('tags:p')
// debug showing function loaded.
debug('(p:sizeCheck) checking..')
// init switch.
this.SwitchManager('pill', 'size', __size)
},
StateCheck: function (__state) {
// stand alone debugger for this module.
let debug = __debug('tags:h1')
// debug showing function loaded.
debug('(h1:sizeCheck) checking..')
// init switch.
this.SwitchManager('pill', 'state', __state)
}
},
created () {
// h1 tag size check
this.SizeCheck(this.size)
this.StateCheck(this.state)
}
I go for mixins like with plain vue.js. Only difference - for global mixins - I include them as a plugin, but first:
Non global mixins
I would create an extra folder for my mixins. For example in a /mixins/testMixin.js
export default {
methods: {
aCommonMethod () {}
}
}
Then import in a layout, page or component and add it via the mixins object:
<script>
import commonMixin from '~/mixins/testMixin.js'
export default {
mixins: [commonMixin]
}
</script>
Global mixins
For example in a new file plugins/mixinCommonMethods.js:
import Vue from 'vue'
Vue.mixin({
methods: {
aCommonMethod () {}
}
})
Include that file then in nuxt.config.js
plugins: ['~/plugins/mixinCommonMethods']
After that you would have the method everywhere available and call it there with this.commonMethod(). But here an advice from the vue.js docs:
Use global mixins sparsely and carefully, because it affects every single Vue instance created, including third party components. In most cases, you should only use it for custom option handling like demonstrated in the example above. It’s also a good idea to ship them as Plugins to avoid duplicate application.
Inject in $root & context
Another possibility would be to Inject in $root & context