Storing in IndexedDB using Pinia - vue.js

Is it possible to store data locally in IndexedDB using Pinia ?
I tried using Pinia persisted state and it stores data locally in LocalStorage as default. I just want to try if it will work with IndexedDB since it has a larger size capacity.

You can implement your own pinia plugin to use whatever storage you want.
Here is an example using localForage.
import { createApp } from 'vue'
import { createPinia, type Store } from 'pinia'
import App from './App.vue'
import localForage from "localforage";
const app = createApp(App)
// Optional
localForage.config({
driver: localForage.INDEXEDDB, // This force IndexedDB as the driver
})
async function indexDbPlugin({ store }: { store: Store }) {
const stored = await localForage.getItem(store.$id + '-state')
if (stored) {
store.$patch(stored)
}
store.$subscribe(() => {
localForage
.setItem(store.$id + '-state', { ...store.$state }) // Destructure to transform to plain object
})
}
const pinia = createPinia()
pinia.use(indexDbPlugin)
app.use(pinia)
app.mount('#app')
https://pinia.vuejs.org/core-concepts/plugins.html#introduction
But with the plugin pinia-plugin-persistedstate you cannot use indexDb as it is asynchronous and this plugin only supports synchronous storage:
https://prazdevs.github.io/pinia-plugin-persistedstate/guide/limitations.html#storage-must-be-synchronous

Related

React Native with async Redux state

I have a React Native app and using Redux Toolkit to store application state. It should load state from remote server during init process.
import { configureStore } from '#reduxjs/toolkit'
import counterReducer, {setAmount} from '../reducers/counterSlice';
import tankReducer, {DEFAULT_TANK} from '../reducers/tankSlice';
import logger from 'redux-logger'
import thunk from 'redux-thunk';
const preloadedState = {
counter:
{
value: 500
},
tank:
{
currentTank: DEFAULT_TANK,
tankList: [DEFAULT_TANK]
}
}
export const store = configureStore({
reducer: {
counter: counterReducer,
tank: tankReducer
},
middleware: (getDefaultMiddleware) => getDefaultMiddleware().concat(logger, thunk),
preloadedState
})
It works fine with static, but i need get that preloadedState from the server with async request. How to do it? I mean can I somehow force configureStore logic to wait until I get async data from the server? Or where I can put async code which update state immediately after I do configureStore?

Access Nuxt custom plugin from Composition API

I am using VueClipboard in my nuxt project.
https://www.npmjs.com/package/vue-clipboard2
I have a plugin file vue-clipboard.js
import Vue from "vue";
import VueClipboard from 'vue-clipboard2';
Vue.use(VueClipboard);
It is imported into nuxt.config
plugins: ['#/plugins/vue-clipboard'],
This sets up a global variable $copyText and in nuxt without the composition API I can do something like
methods: {
async onCopyCodeToClipboard() {
const code = 'code'
await this.$copyText(code)
},
},
However inside the setup using the composition API (#nuxtjs/composition-api) when I write a function I do not have access to this.$copyText
const onCopyCodeToClipboard = async () => {
const code = context.slots.default()[0].elm.outerHTML
// -> Can't use this here - await this.$copyText(code)
}
So how do I make $copyText available to use inside the composition API?
I was able to get this to work via the Nuxt useContext() method:
import { useContext } from '#nuxtjs/composition-api'
export default function () {
const { $copyText } = useContext();
$copyText('code');
}

Can Redux store token sessions in React Native?

I am new to Redux and React Native and would like know if I can implement and store token sessions in Redux for keeping the user logged in after they close and reopen the app. I have found out some people recommend AsyncStorage but my app state is handled with Redux.
This is my Redux store which uses AsyncStorage too.
import { createStore } from 'redux'
import { persistStore, persistReducer } from 'redux-persist'
import AsyncStorage from '#react-native-community/async-storage';
import reducer from './reducers/index'
const persistConfig = {
key: 'root',
version: 0,
storage: AsyncStorage
}
const persistedReducer = persistReducer(persistConfig, reducer)
const store = createStore(persistedReducer)
const persistor = persistStore(store)
export { store, persistor }
Would that be enough to keep a token session as I store other data in the same way?
redux store cannot restore data after closing and reopening the app.
import { AsyncStorage } from 'react-native'
you can store by
AsyncStorage.setItem(userSessionKey, userData)
and restore by
async function restoreSession() {
try {
const data = await AsyncStorage.getItem(userSessionKey)
const userData = JSON.parse(data)
if (userData !== null) {
return userData
} else {
throw new Error('User Data is empty')
}
} catch (error) {
//console.log(error)
return null
}
}
so, when the app starts, before navigating to main app,
restore the data, and add to redux store
Redux can't persist/keep the data of the store/reducer when you kill the application.
But with the help of redux-persist library, redux can persist the data of the reducer/store. Also if you user reudx-persist you don't have to manually create AsyncStorage calls for retrieving initially when app starts redux-persist will handle that for you. You can use different storage engines not just AsyncStorage more info here
In your case you can totally store user session token in redux with the help of redux-persist. Use Whitelist/blacklist in persist config to let redux-persist know which reducer to persist.
e.g.
// BLACKLIST
const persistConfig = {
key: 'root',
storage: storage,
blacklist: ['authReducer'] // navigation will not be persisted
};
// WHITELIST
const persistConfig = {
key: 'root',
storage: storage,
whitelist: ['authReducer'] // only navigation will be persisted
};

Theory: Axios Calls (Specifically for VueJS)

On component mount(), Axios fetches information from the back end. On a production site, where the user is going back and forth between routes it would be inefficient to make the same call again and again when the data is already in state.
How do the pros design their VueJS apps so that unnecessary Axios calls are not made?
Thank you,
If the data is central to your application and being stored in Vuex (assuming that's what you mean by "state"), why not just load it where you initialise your store?
// store.js
import Vue from 'vue'
import Vuex from 'vuex'
import axios from 'wherever'
Vue.use(Vuex)
const store = new Vuex.Store({
state: {
centralData: {}
},
mutations: {
setCentralData (state, centralData) {
state.centralData = centralData
}
},
actions: {
async loadCentralData ({ commit }) {
const { data } = await axios.get('/backend')
commit('setCentralData', data)
}
}
}
// initialise
export const init = store.dispatch('loadCentralData')
export default store
If you need to wait for the dispatch to complete before (for example) mounting your root Vue instance, you can use the init promise
import Vue from 'vue'
import router from 'path/to/router'
import store, { init } from 'path/to/store'
init.then(() => {
new Vue({
store,
router,
// etc
}).$mount('#app')
})
You can import and use the init promise anywhere in order to wait for the data to load.

importing store to a vuejs nuxt project

I'm trying to write a simple plugin for my Vue.js(Nuxt) project. I came across this post Adding Mutations to Vuex store as part of Vue Plugin but still unable to get it working.
Here is my application structure.
~ is root
~/plugins/HTTP/index.js
~/plugins/HTTP/_store/ => index.js, actions.js, getters.js, mutations.js
~/plugins/HTTP/_api/ => index.js
**Global Store**
~/store/index.js
~/store/modules/
~/store/modules/testing => index.js, actions.js, getters.js, mutations.js
in my ~/plugins/HTTP/index.js, I have the following code
import Vue from 'vue';
import store from '~/store';
const HTTP = {
install(vue, { store }){ // Now you plugin depend on store
if(!store){
throw new Error('Please provide vuex plugin.')
}
// register your own vuex module
store.registerModule({store})
}
}
export default HTTP;
Vue.use(HTTP)
In my ~/store/index.js I have the following code:
import Vuex from 'vuex'
import testingModule from './modules/testing'
const state = () => {
return new Vuex.Store({
modules:{
testing: testingModule
}
})
}
export default state
When I try to run it, it gives me the following message:
Cannot destructure property `store` of 'undefined' or 'null'.
What did I do wrong here?
You aren't passing any properties so the error is correct. You need pass in an options object when you tell it to use. It can be empty, but it needs an object.
import Vue from 'vue';
import store from '~/store';
const HTTP = {
install(vue, { store }){ // Now you plugin depend on store
if(!store){
throw new Error('Please provide vuex plugin.')
}
// register your own vuex module
store.registerModule({store})
}
}
export default HTTP;
Vue.use(HTTP, {}) // <---------- Empty object to avoid allow destructuring.