How to access global variable React Native Functional - react-native

I inherited a project, where the original owner uses global variables. The app was made in class-based components, but I have since been using functional components. I much prefer functional components in React-Native.
Anyways, the original user has an API route defined in the global variable. The data structure looks like this, and it is called/instantiated in the constructor of App.js, which is the root of our project:
initGlobalState = async () => {
this.setGlobal({
token: '',
API_ROUTE: API_ROUTE.TEST_API_URL, // TODO
selectedAppId: "1",
maps: [],
assets: [],
places: [],
isLoggedIn: false,
sliderValue: 0,
isSwitchOn: false,
pw: "",
ssid: "",
gatewaySelected: false,
configSelected: "",
envCollection: [],
})
};
Now, when we make API calls, we just reference this.global.API_ROUTE from anywhere in the project, and it returns the value that was set in the above setGlobal. What's confusing is, setGlobal is not defined anywhere...so it must be an installed package that uses it somehow?
When I try to do the same in a functional component, this.global is not recognized in general. Regular global is not even recognized. I am unsure how I would access the global variable in this case, any ideas or thoughts to walk thru this?

Related

How to use vue component across multiple node projects?

I'm trying to build a website builder within the drag-and-drop abilities via using Vue3. So, the user will be playing with the canvas and generate a config structure that going to post the backend. Furthermore, the server-side will generate static HTML according to this config.
Eventually, the config will be like the below and it works perfectly. The config only can have HTML tags and attributes currently. Backend uses h() function to generate dom tree.
My question is: can I use .vue component that will generate on the server side as well? For example, the client-side has a Container.vue file that includes some interactions, styles, etc. How can the backend recognize/resolve this vue file?
UPDATE:
Basically, I want to use the Vue component that exists on the Client side on the backend side to generate HTML strings same as exactly client side. (including styles, interactions etc).
Currently, I'm able to generate HTML string via the below config but want to extend/support Vue component itself.
Note: client and server are completely different projects. Currently, server takes config and runs createSSRApp, renderToString methods.
Here is the gist of how server would handle the API:
https://gist.github.com/yulafezmesi/162eafcf7f0dcb3cb83fb822568a6126
{
id: "1",
tagName: "main",
root: true,
type: "container",
properties: {
class: "h-full",
style: {
width: "800px",
transform: "translateZ(0)",
},
},
children: [
{
id: "9",
type: "image",
tagName: "figure",
interactive: true,
properties: {
class: "absolute w-28",
style: {
translate: "63px 132px",
},
},
},
],
}
This might get you started: https://vuejs.org/guide/scaling-up/ssr.html#rendering-an-app
From the docs:
// this runs in Node.js on the server.
import { createSSRApp } from 'vue'
// Vue's server-rendering API is exposed under `vue/server-renderer`.
import { renderToString } from 'vue/server-renderer'
const app = createSSRApp({
data: () => ({ count: 1 }),
template: `<button #click="count++">{{ count }}</button>`
})
renderToString(app).then((html) => {
console.log(html)
})
I guess extract the template from request or by reading the submitted Vue file and use that as the template parameter value

Vue composition api - composable with global state that uses component props

In my app there is a composable called useLocalization. It provides translated strings based on the user's language preferences, and is used throughout the app.
The problem is, that useLocalization can be configured, and accepts the following arguments, but I don't know what the best way of passing these arguments from the root component to the composable:
interface ILocalizationProps {
currentLocale: Ref<string>
fallbackLocale: Ref<string>
locales: Ref<Partial<ILocale>[]>
}
The root component that uses useLocalization accepts the same arguments as props, so the consuming App can configure/override the language used.
const DEFAULT_LANG = 'en'
export const withLocalizationProps = () => ({
currentLocale: { type: String, default: null },
fallbackLocale: { type: String, default: DEFAULT_LANG },
locales: { type: Array as () => Partial<ILocale>[], default: () => [] },
})
How can I initialize my composable with the props passed to the root component? Here's what I tried/issues I found:
useLocalization(props) doesn't work here, as the props are not available deeper in the component tree.
Using provide/inject I can use props in the provide part, and get the correctly configured version with inject, but this prevents my from using useLocalization in the root component, as the injection is not available.
Use a hacky solution such as in vee-validate / injectWithSelf. Even then, The signature of the composable would be useLocalization(props?: IProps), and my root component has to be the first one to call this function with the props.
Use a helper such as createInjectionState, but it's the same problem as 2.
Is there a best way to solve this? Composable that don't depend component state, such as useMouse work great, but (globally) configurable composables cause the afore mentioned issues :(
In that case, it'd be better to use provide/inject than using props.
Here's the reference.
https://vuejs.org/guide/components/provide-inject.html

Call context in nuxt not working in v-bind

I have question related to using context or prototype in Nuxt
I create a constant for 'modal' name like this:
export default Object.freeze({
MODAL_SHOWPRO: "MODAL_SHOWPRO",
})
I also created constant.js in plugin folder and already added to nuxt config.
import modals from '#/constants/modal';
export default ({ app }, inject) => {
inject('modalName', modals)
}
In component I can't call value from v-bind, it said : undefined MODAL_SHOWPRO
<Popup :id="$modalName.MODAL_SHOWPRO" />
but I can call it from $emit function something like this:
#click="$nuxt.$emit('showModal', {id: $modalName.MODAL_SHOWPRO})"
Can you let me know why and how to fix it?
Notice: It will work if:
I make data
{
modal: ''
}
and add to created:
async created() {
this.modalName = await this.$modalName
}
Nuxt is a meta-framework aimed at providing an universal app (server then client side). So, you need to think about both server and client.
In your code, you specified ssr: false, this is outdated and should rather be mode: 'client'. But setting so is still false because it means that the ENUM will not be available on the server (hence the error).
Setting it like this is more appropriate (regarding the nature of the plugin) and also fixes the issue
plugins: ['~/plugins/constant.js'],
More on Nuxt plugins: https://nuxtjs.org/docs/directory-structure/plugins#plugins-directory

Pass an object as param to router.push (vue-router#4.05)

Problem
router.push({name:"Order", params: {obj: {}})
fails to push obj: {}, instead the route receives obj: '[object Object]'
Situation
I have a route setup like this
{
path: '/Order',
name: 'Order',
component: () => import("../views/Order.vue"),
props: route => {
return route.params.obj // '[object Object]'
},
}
this results in props not being defined in Order.vue
Expected Result
{
...
props: route => {
return route.params.obj // '{}'
},
}
Based on this answer objects work in older versions
What I've tested
I've used jest to inspect the arguments passed to router.push and they appear as they should: {name:"Order", params: {obj: {}}
Any Ideas?
Passing objects to params was never supported in Vue-router
It sort of worked in Router v3 with $router.push - the target component received the object. BUT as soon as user started using browser navigation (Back button) OR copy/pasting URL's, this solution was broken (you can try it here - just click the button and then use back and forward controls of the frame)
As a rule of thumb - if you want to pass anything to the target route/component, such data must be defined as parameters in the route definition so it can be included directly in the URL. Alternatives are passing data in a store, query params (objects needs to be serialized with JSON.stringify), or using the history state.
This was true for Vue-router v3 and is still for Vue-router v4
Note
Just to explain alternatives. By "store" I do not mean just Vuex. I understand Vuex can be overkill for some small applications. I personally prefer Pinia over existing Vuex. And you can create global state solution yourself with composition API very easily...

Vue-multiselect inconsistent reactive options

So I'm building an application using Laravel Spark, and therefore taking the opportunity to learn some Vue.js while I'm at it.
It's taken longer for me to get my head around it than I would have liked but I have nearly got Vue-multiselect working for a group of options, the selected options of which are retrieved via a get request and then updated.
The way in which I've got this far may well be far from the best, so bear with me, but it only seems to load the selected options ~60% of the time. To be clear - there are never any warnings/errors logged in the console, and if I check the network tab the requests to get the Tutor's instruments are always successfully returning the same result...
I've declared a global array ready:
var vm = new Vue({
data: {
tutorinstruments: []
}
});
My main component then makes the request and updates the variable:
getTutor() {
this.$http.get('/get/tutor')
.then(response => {
this.tutor = response.data;
this.updateTutor();
});
},
updateTutor() {
this.updateTutorProfileForm.profile = this.tutor.profile;
vm.tutorinstruments = this.tutor.instruments;
},
My custom multiselect from Vue-multiselect then fetches all available instruments and updates the available instruments, and those that are selected:
getInstruments() {
this.$http.get('/get/instruments')
.then(response => {
this.instruments = response.data;
this.updateInstruments();
});
},
updateInstruments() {
this.options = this.instruments;
this.selected = vm.tutorinstruments;
},
The available options are always there.
Here's a YouTube link to how it looks if you refresh the page over and over
I'm open to any suggestions and welcome some help please!
Your global array var vm = new Vue({...}) is a separate Vue instance, which lives outside your main Vue instance that handles the user interface.
This is the reason you are using both this and vm in your components. In your methods, this points to the Vue instance that handles the user interface, while vm points to your global array that you initialized outside the Vue instance.
Please check this guide page once more: https://v2.vuejs.org/v2/guide/instance.html
If you look at the lifecycle diagram that initializes all the Vue features, you will notice that it mentions Vue instance in a lot of places. These features (reactivity, data binding, etc.) are designed to operate within a Vue instance, and not across multiple instances. It may work once in a while when the timing is right, but not guaranteed to work.
To resolve this issue, you can redesign your app to have a single Vue instance to handle the user interface and also data.
Ideally I would expect your tutorinstruments to be loaded in a code that initializes your app (using mounted hook in the root component), and get stored in a Vuex state. Once you have the data in your Vuex state, it can be accessed by all the components.
Vuex ref: https://vuex.vuejs.org/en/intro.html
Hope it helps! I understand I haven't given you a direct solution to your question. Maybe we can wait for a more direct answer if you are not able to restructure your app into a single Vue instance.
What Mani wrote is 100% correct, the reason I'm going to chime in is because I just got done building a very large scale project with PHP and Vue and I feel like I'm in a good position to give you some advice / things I learned in the process of building out a PHP (server side) website but adding in Vue (client side) to the mix for the front end templating.
This may be a bit larger than the scope of your multiselect question, but I'll give you a solid start on that as well.
First you need to decide which one of them is going to be doing the routing (when users come to a page who is handling the traffic) in your web app because that will determine the way you want to go about using Vue. Let's say for the sake of discussion you decide to authenticate (if you have logins) with PHP but your going to handle the routing with Vue on the front end. In this instance your going to want to for sure have one main Vue instance and more or less set up something similar to this example from Vue Router pretending that the HTML file is your PHP index.php in the web root, this should end up being the only .php file you need as far as templating goes and I had it handle all of the header meta and footer copyright stuff, in the body you basically just want one div with the ID app.
Then you just use the vue router and the routes to load in your vue components (one for each page or category of page works easily) for all your pages. Bonus points if you look up and figure using a dynamic component in your main app.vue to lazy load in the page component based on the route so your bundle stays small.
*hint you also need a polyfill with babel to do this
template
<Component :is="dynamicComponent"/>
script
components: {
Account: () => import('./Account/Account.vue'),
FourOhFour: () => import('../FourOhFour.vue')
},
computed: {
dynamicComponent() {
return this.$route.name;
}
},
Now that we are here we can deal with your multiselect issue (this also basically will help you to understand an easy way to load any component for Vue you find online into your site). In one of your page components you load when someone visits a route lets say /tutor (also I went and passed my authentication information from PHP into my routes by localizing it then using props, meta fields, and router guards, its all in that documention so I'll leave that to you if you want to explore) on tutor.vue we will call that your page component is where you want to call in multiselect. Also at this point we are still connected to our main Vue instance so if you want to reference it or your router from tutor.vue you can just use the Vue API for almost anything subbing out Vue or vm for this. But the neat thing is in your main JS file / modules you add to it outside Vue you can still use the API to reference your main Vue instance with Vue after you have loaded the main instance and do whatever you want just like you were inside a component more or less.
This is the way I would handle adding in external components from this point, wrapping them in another component you control and making them a child of your page component. Here is a very simple example with multiselect pretend the parent is tutor.vue.
Also I have a global event bus running, thought you might like the idea
https://alligator.io/vuejs/global-event-bus/
tutor.vue
<template>
<div
id="user-profile"
class="account-content container m-top m-bottom"
>
<select-input
:saved-value="musicPreviouslySelected"
:options="musicTypeOptions"
:placeholder="'Choose an your music thing...'"
#selected="musicThingChanged($event)"
/>
</div>
</template>
<script>
import SelectInput from './SelectInput';
import EventBus from './lib/eventBus';
export default {
components: {
SelectInput
},
data() {
return {
profileLoading: true,
isFullPage: false,
isModalActive: false,
slackId: null,
isActive: false,
isAdmin: false,
rep: {
id: null,
status: '',
started: '',
email: '',
first_name: '',
},
musicTypeOptions: []
};
},
created() {
if (org.admin) {
this.isAdmin = true;
}
this.rep.id = parseInt(this.$route.params.id);
this.fetchData();
},
mounted() {
EventBus.$on('profile-changed', () => {
// Do something because something happened somewhere else client side.
});
},
methods: {
fetchData() {
// use axios or whatever to fetch some data from the server and PHP to
// load into the page component so say we are getting the musicTypeOptions
// which will be in our selectbox.
},
musicThingChanged(event) {
// We have our new selection "event" from multiselect so do something
}
}
};
</script>
this is our child Multiselect wrapper SelectInput.vue
<template>
<multiselect
v-model="value"
:options="options"
:placeholder="placeholder"
label="label"
track-by="value"
#input="inputChanged" />
</template>
<script>
import Multiselect from 'vue-multiselect';
export default {
components: { Multiselect },
props: {
options: {
type: [Array],
default() {
return [];
}
},
savedValue: {
type: [Array],
default() {
return [];
}
},
placeholder: {
type: [String],
default: 'Select Option...'
}
},
data() {
return {
value: null
};
},
mounted() {
this.value = this.savedValue;
},
methods: {
inputChanged(selected) {
this.$emit('selected', selected.value);
}
}
};
</script>
<style scoped>
#import '../../../../../node_modules/vue-multiselect/dist/vue-multiselect.min.css';
</style>
Now you can insure you are manging the lifecycle of your page and what data you have when, you can wait until you get musicTypeOptions before it will be passed to SelectInput component which will in turn set up Multiselect or any other component and then handle passing the data back via this.$emit('hihiwhatever') which gets picked up by #hihiwhatever on the component in the template which calls back to a function and now you are on your way to do whatever with the new selection and pass different data to SelectInput and MultiSelect will stay in sync always.
Now for my last advice, from experience. Resist the temptation because you read about it 650 times a day and it seems like the right thing to do and use Vuex in a setup like this. You have PHP and a database already, use it just like Vuex would be used if you were making is in Node.js, which you are not you have a perfectly awesome PHP server side storage, trying to manage data in Vuex on the front end, while also having data managed by PHP and database server side is going to end in disaster as soon as you start having multiple users logged in messing with the Vuex data, which came from PHP server side you will not be able to keep a single point of truth. If you don't have a server side DB yes Vuex it up, but save yourself a headache and wait to try it until you are using Node.js 100%.
If you want to manage some data client side longer than the lifecycle of a page view use something like https://github.com/gruns/ImmortalDB it has served me very well.
Sorry this turned into a blog post haha, but I hope it helps someone save themselves a few weeks.