How do you implement AsynSelect with isMilti in Typescript? #react-select - react-select

I can't seem to get isMulti to work with Aysync select. I've tried a number of iterations but nothing works. Below is my code. Once I uncomment the isMulti line, thats whne things break. I tried to create a new array type to see if that helps but it doesn't.
I also have another issue where the options box doesn't load the options from the promise function but only does so when I delete a character from the input (it's using the cached results to populated the dropdown at that point).
import { useState } from 'react';
import { FunctionComponent } from 'react';
import AsyncSelect from 'react-select/async';
import ValueType from 'react-select';
import { getGoogleAutoComplete } from './services';
import map from '../../assets/map.svg';
import './LocationInput.styles.scss';
type OptionType = {
value: string;
label: string;
}
type OptionTypeArray = Array<OptionType>;
const LocationInput: FunctionComponent = () => {
const [locations, setLocations] = useState<ValueType<OptionType | OptionTypeArray>>();
const [query, setQuery] = useState("");
const handleChange = (option: ValueType<OptionType | OptionTypeArray> | null) => {
if (option != null){setLocations(option)};
console.log('im in handleChange!')
console.log(option)
};
async function promiseOptions(): Promise<any>{
return await getGoogleAutoComplete(query);
}
return (
<div className='location-input-container'>
<div className='map-icon'>
<img src={map} alt=''/>
</div>
<div className='location-input'>
<AsyncSelect
//isMulti={true}
cacheOptions
value={locations}
defaultOptions
placeholder='Enter a City or ZIP code'
onChange={(option) => handleChange(option)}
onInputChange={(value) => setQuery(value)}
closeMenuOnSelect={true}
noOptionsMessage={() => 'No Match Found'}
isClearable={true}
loadOptions={promiseOptions} />
</div>
</div>
)
}
export default LocationInput;

I was able to find a solution. However, I had to use Type Assertion to make it workout which isn't the ideal approach. If anyone has any other suggestions I'd greaty appreacite it.
import { useState, FunctionComponent } from 'react';
import AsyncSelect from 'react-select/async';
import { getGoogleAutoComplete } from './services';
import OptionTypeBase from 'react-select';
import { OptionsType } from 'react-select/src/types';
import makeAnimated from "react-select/animated";
import map from '../../assets/map.svg';
import './LocationInput.styles.scss';
const LocationInput: FunctionComponent = () => {
const [locations, setLocations] = useState<OptionsType<OptionTypeBase>>();
const [query, setQuery] = useState("");
const handleChange = (option: OptionsType<OptionTypeBase>) => {
setLocations(option);
console.log('im in handleChange!')
console.log(option)
};
async function promiseOptions(value:string): Promise<any>{
return new Promise(resolve => resolve(getGoogleAutoComplete(value)));
}
//get animated components wrapper
const animatedComponents = makeAnimated();
return (
<div className='location-input-container'>
<div className='map-icon'>
<img src={map} alt=''/>
</div>
<div className='location-input'>
<AsyncSelect
isMulti={true}
components={animatedComponents}
cacheOptions
placeholder='Enter a City or ZIP code'
onChange={(option) => handleChange(option as OptionsType<OptionTypeBase>)}
closeMenuOnSelect={true}
noOptionsMessage={() => 'No Match Found'}
loadOptions={promiseOptions} />
</div>
</div>
)
}
export default LocationInput;

Related

Problem with handling Browser Cookies in vue.js

Well, I have a problem with a plugin handling browser cookies in vue.js
I installed plugin and imported it in main.js. I configured it too like below:
import { createApp } from "vue";
import { createPinia } from "pinia";
import { globalCookiesConfig } from "vue3-cookies";
import App from "./App.vue";
import router from "./router";
import { useMainStore } from "#/stores/main.js";
import { useStyleStore } from "#/stores/style.js";
import { darkModeKey, styleKey } from "#/config.js";
import "./css/main.css";
globalCookiesConfig({
expireTimes: "30d",
path: "/",
domain: "",
secure: true,
sameSite: "None",
});
/* Init Pinia */
const pinia = createPinia();
/* Create Vue app */
createApp(App).use(router).use(pinia).mount("#app");
/* Init Pinia stores */
const mainStore = useMainStore(pinia);
const styleStore = useStyleStore(pinia);
/* Fetch sample data */
mainStore.fetch("clients");
mainStore.fetch("history");
/* App style */
styleStore.setStyle(localStorage[styleKey] ?? "basic");
/* Dark mode */
if (
(!localStorage[darkModeKey] &&
window.matchMedia("(prefers-color-scheme: dark)").matches) ||
localStorage[darkModeKey] === "1"
) {
styleStore.setDarkMode(true);
}
/* Default title tag */
const defaultDocumentTitle = "SOFT-KOMP";
/* Set document title from route meta */
router.afterEach((to) => {
document.title = to.meta?.title
? `${to.meta.title} — ${defaultDocumentTitle}`
: defaultDocumentTitle;
});
And In my LoginView.vue i inserted this line in function:
this.$cookies.set('hover-time','1s');
Unfortunately I receive this:
runtime-core.esm-bundler.js:218 Uncaught TypeError: Cannot read properties of undefined (reading '$cookies')
So this is my LoginView.vue file. And I tried to test some of other sessions and cookie plugins but results was always the same. PRobably Im not installing this plugin properly in the component or something?
<script setup>
import { reactive } from "vue";
import { useRouter } from "vue-router";
import { mdiAccount, mdiAsterisk } from "#mdi/js";
import SectionFullScreen from "#/components/SectionFullScreen.vue";
import CardBox from "#/components/CardBox.vue";
import FormCheckRadio from "#/components/FormCheckRadio.vue";
import FormField from "#/components/FormField.vue";
import FormControl from "#/components/FormControl.vue";
import BaseButton from "#/components/BaseButton.vue";
import BaseButtons from "#/components/BaseButtons.vue";
import LayoutGuest from "#/layouts/LayoutGuest.vue";
const form = reactive({
login: "john.doe",
pass: "highly-secure-password-fYjUw-",
remember: true,
});
const router = useRouter();
const submit = () => {
router.push("/dashboard");
};
const login = () =>{
this.$cookies.set('hover-time','1s');
}
</script>
<template>
<LayoutGuest>
<SectionFullScreen v-slot="{ cardClass }" bg="purplePink">
<CardBox :class="cardClass" is-form #submit.prevent="submit">
<FormField label="Login" help="Please enter your login">
<FormControl
v-model="form.login"
:icon="mdiAccount"
name="login"
autocomplete="username"
/>
</FormField>
<FormField label="Password" help="Please enter your password">
<FormControl
v-model="form.pass"
:icon="mdiAsterisk"
type="password"
name="password"
autocomplete="current-password"
/>
</FormField>
<FormCheckRadio
v-model="form.remember"
name="remember"
label="Remember"
:input-value="true"
/>
<template #footer>
<BaseButtons>
<BaseButton #click='login' type="submit" color="info" label="Login" />
<BaseButton to="/dashboard" color="info" outline label="Back" />
</BaseButtons>
</template>
</CardBox>
</SectionFullScreen>
</LayoutGuest>
</template>
Well, what you have to is, first import the use in your setup in your LoginView.vue:
import { useCookies } from "vue3-cookies";
Then you can use it like this:
const { cookies } = useCookies();
and then cookies is accessible across your setup.
See answer and comment if you need more info.

Cypress component testing with Vue-I18N

I am trying to use Cypress for component testing in my Vue app. I am using the vue-i18n library to provide localisation for the app. When attempting to test the rendering of my loading spinner component, I am getting the following error from the vue-i18n library:
SyntaxError: Need to install with `app.use` function
at createCompileError (http://localhost:5173/__cypress/src/node_modules/.vite/deps/vue-i18n.js?v=64756eb2:183:17)
at createI18nError (http://localhost:5173/__cypress/src/node_modules/.vite/deps/vue-i18n.js?v=64756eb2:2625:10)
at useI18n (http://localhost:5173/__cypress/src/node_modules/.vite/deps/vue-i18n.js?v=64756eb2:4231:11)
Previously to this, I was getting an error from Pinia. I resolved this by adding the following to cypress/support/component.ts:
import { createPinia, setActivePinia } from 'pinia';
setActivePinia(
createPinia()
);
My LoadingSpinner component code is as follows:
<script setup lang="ts">
import { computed } from "#vue/reactivity";
import { useLocaleStore } from "#/stores/locale";
//props
const { i18n } = useLocaleStore();
</script>
<template>
<div class="d-flex justify-content-center m-5">
<div
class="spinner-border text-primary"
:style="{ width, height }"
role="status"
>
<span class="visually-hidden">{{ i18n.t("loading") }}</span>
</div>
</div>
</template>
And the test code:
import LoadingSpinner from "../../src/components/ui/LoadingSpinner.vue";
describe("LoadingSpinner", () => {
it("renders", () => {
cy.mount(LoadingSpinner);
});
});
/stores/locale:
import { computed } from "vue";
import { defineStore } from "pinia";
import { useI18n } from "vue-i18n";
export const useLocaleStore = defineStore("locale", () => {
const i18n = useI18n({
useScope: "global",
inheritLocale: true,
});
const currentLocale = computed(() => i18n.locale);
const locales = computed(() => i18n.availableLocales);
return { i18n, currentLocale, locales };
});
I found this github release that implies I need to add vue-i18n as a plugin to the mount() call, but I can't work out how to do it. Does anyone know a solution?

Can't get v-model work with Composition API and Vuex

I've read several posts on stackoverflow and other websites, but still can't figure out what's going wrong in my case.
I'm building an app following composition api approach and using a variable called modelStartDate (which I initiate at Jan 3, 2022). This is how my store looks:
import { createStore } from 'vuex'
export default createStore({
state: {
modelStartDate: new Date(2022, 0, 3)
},
mutations: {
modelStartDateMutation(state, newDate) {
state.modelStartDate = newDate
}
},
actions: {
},
getters: {
},
modules: {
}
})
In the relevant Vue file, I have the following code snippet:
<template>
<nav class="left-bar">
<div class="block" id="modelStartDate">
<label>Model start date</label>
<input type="date" v-model="modelStartDateProxy" />
</div>
<p>{{ modelStartDate }}</p>
</nav>
</template>
<script>
import { ref } from '#vue/reactivity'
import { useStore } from 'vuex'
import { computed } from '#vue/runtime-core'
export default {
setup() {
const store = useStore()
const modelStartDateProxy = computed({
get: () => store.state.modelStartDate,
set: (newDate) => store.commit("modelStartDateMutation", newDate)
})
const modelStartDate = store.state.modelStartDate
return { modelStartDateProxy, modelStartDate }
}
}
</script>
When I run the page, the paragraph tag prints the right date, however the input tag, where the user can change the date, is empty (I was expecting Jan 3, 2022 to be pre-selected). When the date is changed, nothing seems to change in the app. I'm getting no errors. Any idea what I'm doing incorrectly?
Also, can I access store's modelStartDate state without having to define it separately (redundantly?) in the vue setup() section?
First, I don't know which tutorial you read. But to me, the problem is here:
const modelStartDateProxy = computed({
get: () => store.state.modelStartDate,
set: (newDate) => store.commit("modelStartDateMutation", newDate)
})
const modelStartDate = store.state.modelStartDate
The snippet
const modelStartDateProxy = computed({
get: () => store.state.modelStartDate,
set: (newDate) => store.commit("modelStartDateMutation", newDate)
})
is weird to me.
Duplicate of store.state.modelStartDate. DRY.
<p>{{ modelStartDate }}</p> render data from const modelStartDate = store.state.modelStartDate. But the data was only assign once. So the new value was not render on input was changed.
Solution:
const modelStartDate = computed(() => store.state.modelStartDate);
You can take a look at this playground.
The html element input returns a string: "YYYY-MM-DD". Therefore you need the syntax new Date(value)
Take a look at this playground
<template>
<label>Model start date</label>
<input type="date" v-model="modelStartDateProxy" />
<p>{{ modelStartDateProxy }}</p>
</template>
<script>
import { store } from './store.js' //mock-up store
import { ref, computed } from 'vue'
export default {
setup() {
const modelStartDateProxy = computed({
get: () => store.state.modelStartDate,
set: (newDate) => store.commit(newDate) // Use real Vuex syntax
})
return { modelStartDateProxy }
}
}
</script>
//Mock-up Store (not real vuex)
import {reactive} from 'vue'
export const store = reactive({
state: {
modelStartDate: new Date(2022, 0, 3)
},
commit: (value) => store.state.modelStartDate = new Date(value) // new Date(value)
})

Is there a way to share reactive data between random components in Vue 3 Composition API?

Having some reactive const in "Component A," which may update after some user action, how could this data be imported into another component?
For example:
const MyComponent = {
import { computed, ref } from "vue";
setup() {
name: "Component A",
setup() {
const foo = ref(null);
const updateFoo = computed(() => foo.value = "bar");
return { foo }
}
}
}
Could the updated value of 'foo' be used in another Component without using provide/inject?
I am pretty new in the Vue ecosystem; kind apologies if this is something obvious that I am missing here.
One of the best things about composition API is that we can create reusable logic and use that all across the App. You create a composable functions in which you can create the logic and then import that into the components where you want to use it. Not only does this make your component much cleaner but also your APP much more maintainable. Below is a simple example of counter to show how they can be used. You can find working demo here:
Create a composable function for counter:
import { ref, computed } from "vue";
const counter = ref(0);
export const getCounter = () => {
const incrementCounter = () => counter.value++;
const decrementCounter = () => counter.value--;
const counterPositiveOrNegitive = computed(() =>
counter.value >= 0 ? " Positive" : "Negitive"
);
return {
counter,
incrementCounter,
decrementCounter,
counterPositiveOrNegitive
};
};
Then you can import this function into your components and get the function or you want to use. Component to increment counter.
<template>
<div class="hello">
<h1>Component To Increment Counter</h1>
<button #click="incrementCounter">Increment</button>
</div>
</template>
<script>
import { getCounter } from "../composables/counterExample";
export default {
name: "IncrementCounter",
setup() {
const { incrementCounter } = getCounter();
return { incrementCounter };
},
};
</script>
Component to decrement counter:
<template>
<div class="hello">
<h1>Component To Decrement Counter</h1>
<button #click="decrementCounter">Decrement</button>
</div>
</template>
<script>
import { getCounter } from "../composables/counterExample";
export default {
name: "DecrementCounter",
setup() {
const { decrementCounter } = getCounter();
return { decrementCounter };
},
};
</script>
Then in the main component, you can show the counter value.
<template>
<img alt="Vue logo" src="./assets/logo.png" />
<div class="counters">
<IncrementCounter />
<DecrementCounter />
</div>
<h3>Main Component </h3>
<p>Counter: {{ counter }}</p>
<p>{{ counterPositiveOrNegitive }}</p>
</template>
<script>
import IncrementCounter from "./components/IncrementCounter.vue";
import DecrementCounter from "./components/DecrementCounter.vue";
import { getCounter } from "./composables/counterExample";
export default {
name: "App",
components: {
IncrementCounter: IncrementCounter,
DecrementCounter: DecrementCounter,
},
setup() {
const { counter, counterPositiveOrNegitive } = getCounter();
return { counter, counterPositiveOrNegitive };
},
};
Hope this was somewhat helpful. You can find a working example here:
https://codesandbox.io/s/vue3-composition-api-blfpj

Attempted import error: 'signInUserWithEmailAndPassword' is not exported from 'firebase/auth'

please i need your help
I'm using Firebase 9.02 and i'm facing a problem when i try to import signInUserWithEmailAndPassword to sign in my users with Firebase authentication module
Is it a problem in the version 9 of Firebase ?
Thank you in advance
import React, { useRef } from "react";
import "./SignUpScreen.css";
import auth from "../firebase";
import {
createUserWithEmailAndPassword,
signInUserWithEmailAndPassword,
} from "firebase/auth";
// import auth from "../firebase";
const SignUpScreen = () => {
const emailRef = useRef(null);
const passwordRef = useRef(null);
const signIn = (e) => {
e.preventDefault();
signInUserWithEmailAndPassword(auth, emailRef, passwordRef)
.then((userCredential) => {
// Signed in
const user = userCredential.user;
// ...
})
.catch((error) => {
alert(error);
});
};
return (
<div className='signupScreen'>
<form>
<h1>Sign In</h1>
<input
ref={emailRef}
className='signin'
type='email'
placeholder='Email'
/>
<input
ref={passwordRef}
className='signin'
type='password'
placeholder='Password'
/>
<button onClick={signIn} className='signin' type='submit'>
Sign In
</button>
</form>
</div>
);
};
export default SignUpScreen;```
No, the error is not a problem with version 9 of the Firebase SDK.
The issue is that the method is wrongly spelt.
It's signInWithEmailAndPassword and not signInUserWithEmailAndPassword.
Checkout the documentation