'Watching` localStorage for a change - vue.js

I have a vue app which has a feature than can change the theme, for example, light/dark,
Initially, I've set in my localStorage.setItem['app_theme', 'light'],
I have my app theme changer function in the Header.vue component, and after clicking the theme changer toggle button, it also changes the localStorage['app_theme'] = 'dark'.
Now, in my other components, I have some computed values/variables/properties like this:
...
computed() {
app_card() {
return localStorage.getItem('app_theme') === 'dark' ? 'card-dark' : 'card-light'; //these are some classes with their respective theme css
},
app_text() {
return localStorage.getItem('app_theme') === 'dark' ? 'text-dark' : 'text-light'; //these are some classes with their respective theme css
}
}
...
I've thought about using polling to get the localStorage.getItem('app_theme') value every 2 secs, but I think this'll slow down my app.
Are there any other alternatives to listen for localstorage item change without polling?

This is exactly what the observer pattern is for.
You create an event "OnAppThemeChange". You can subscribe to that event with all your components.
Then whenever your App Theme changed you call your event, and all the components will refresh their App Theme.
This removes the need of Refreshing the theme every 2 seconds. It will only refresh when you actually change the theme.
Usefull links:
https://developer.mozilla.org/de/docs/Web/Guide/Events/Creating_and_triggering_events
https://refactoring.guru/design-patterns/observer

Related

Change state from other component (without passing setState function)

I have a quite decent background in android but now I am starting digging into react native and I am really confused with the ways to change the state of a component through hooks and set state function.
To the point, I have my main screen and I have many small components which change visibility. This is done by letting the user change some filter settings within dialogs. So the suggested way to do that is by having a hook in my main screen with a list that holds the values for the visibility of each component. But since I change the visibility of the components from inside the modals, every time I want to show a modal I will have to pass in a different function(for example setComponentEnable or setComponentDisabled) to set the state for each component. So my main screen will be polluted from all these small functions. Also I should not forget to mention that my modals are consisted from many smaller components and I will have to pass as deep as it goes the proper function to match the user action.
So my question is, is there a way to do this thing without polluting my main with all these small functions and make it possible for the main screen to update every time the user change the filters within the modals?
I already read about context but the docs say:
Context is designed to share data that can be considered “global” for a tree of React components, such as the current authenticated user, theme, or preferred language.
So I dont think that this should be a great case for context use.
What I am trying to do now is create a hook with a list
const [isibility, setVisibility] = useState([]);
create visibility handler functions
const setVisibilityEnable = () => {
...
}
and pass it into my modal.
<MyModal
visibilityHandler={setVisibilityEnable}/>
Is there a way to manipulate the state without passing all these callbacks to the modals? Or maybe is there anyone that can suggest a better and clean solution to avoid end up having a really huge main screen?
you can include all the settings in one object and pass that object to all the components. Then each component will then modify that object accordingly.
const defaultVisibility = {
childComponentOne: true,
childComponentTwo: true,
};
const [visibilityObject, setVisibilityObject] = useState(defaultVisibility);
pass both the function and the object into your child components:
<ChildComponentOne visibilityObject={visibilityObject} setVisibilityObject={setVisibilityObject} />
Then in your child component, you set the visibility like so:
setVisibilityObject({...visibilityObject, childComponentOne: false});
Why you don't just pass a property to your modal and check if changed in oncomponentdidchange method?
componentDidUpdate(prevProps) {
if (this.props.yourPoperty!== prevProps.yourPoperty) {
//do your visibility stuff
}
}
Alternatively you can do it with redux when you connect your components to the store.

Nuxt js & Vue : The best way to implement a light / Dark mode in a relatively large project?

suppose that in a large project ( on Nuxt JS Framework ) with a number of pages and components, we want to implement the dark / light mode of the website template in an optimal and professional way with toggle a button.
In total, the template is about 8 to 10 colors for each mode, and a few other props, such as shadows and borders, are set to change in each mode. You can change the color of the template quickly and gently by pressing a button.
What do you think is the most optimal, most professional, fastest and easiest way to do this ???
The project is being developed on Nuxt Js framework.
Friends, please explain your suggestion and solution in detail so that it is useful and practical for others :)
Thanks for your comments and answers :)
Maybe you can give a try to the nuxt community module: https://github.com/nuxt-community/color-mode-module
Demo web site : https://color-mode.nuxtjs.app/
#Ifaruki How can this be used in Vue JS components? For example, suppose we want to change the value of the variables in the root by pressing a button in the header? How should we write this method? Suppose there are 10 variables in root and we want to change all 10 variables by pressing a button, how should we do that?
Basically, is this the right way to make dark / light, and does it work well? Is this method correct?
root.style.setProperty('--background', "green");
you can use this piece of code in your styles :
:root {
--color-primary: blue;
--color-secondary: black;
}
:root[data-mode="dark"] {
--color-primary: aqua;
--color-secondary: white;
}
and then set data-mode="dark" to your document root element (HTML) like this:
<script setup lang="ts">
onMounted(() => {
if (
!("theme" in localStorage) &&
window.matchMedia("(prefers-color-scheme: dark)").matches
) {
document.documentElement.setAttribute("data-mode", "dark");
}
if ("theme" in localStorage) {
document.documentElement.setAttribute("data-mode", localStorage.theme);
}
})
</script>
this lifecycle checks the user's localStorage or system and sets mode based on these two if the user didn't set the mode before it set mode based on the system
and can toggle mode with this function:
<script setup>
const changeMode = (mode) => {
document.documentElement.setAttribute("data-mode", mode);
localStorage.theme = mode
}
</script>
the mode can be dark or light.

Scrolling to v-expansion-panel opening

I'm trying to build a mobile small application using v-expansion-panels to display a list.
The idea is that when the user adds a new item in such list it will open the new panel and scroll down to such new panel.
I found a goTo() method in the $vuetify variable, unfortunatly the v-expansion-panels transition (the "opening") take some time and the goTo() won't completely scroll down because of the scrollbar height changes.
So from my understanding I need to detect the end of the transition (enter/afterEnter hook).
Per the vuetifyjs documentation, I could hope to have a "transition" property on my component. (Which isn't the case anyway). But such property is only a string so I can't hook into it.
My other idea is to, somehow, find the transition component and hook into it. Unfortunatly I have trouble understanding el/vnode and the way vuejs is building is tree like the vue-devtool show and I can't get the transition component. When debugging (in the enter hook callback of the transition) it is like the component/el/vnode has a parent but isn't the child of anybody.
Is there a way to do what I'm looking for?
Here is a jsfiddler of what I currently do: https://jsfiddle.net/kdgn80sb/
Basically it is the method I'm defining in the Vue:
methods: {
newAlarm: function() {
const newAlarmPanelIndex = this.alarms.length - 1;
this.alarms.push({title: "New line"});
this.activePanelIndex = newAlarmPanelIndex;
// TODO:
this.$vuetify.goTo(this.$refs.alarmPanels[newAlarmPanelIndex]);
}
}
Firstly you should open manually panel and then use goTo with a timeout.
It works for me, just give some time to a panel to open.
<v-expansion-panels v-model="alarmPanels">
<v-expansion-panel>
<div id="example" />
</v-expansion-panel>
</v-expansion-panels>
this.alarmPanels.push(0); // Use index of expansion-panel
setTimeout(() => {
this.$vuetify.goTo(`#${resultId}`);
}, 500);

React Select 2 - Portal menu positioning

Is it possible to take control of React-Select 2's menu positioning? I'm using it inside React Virtualized table rows but then I'm rendering the dropdown to a portal outside of the row. I have set menuPosition to fixed but React-Select 2 has delayed automatic positioning which is far too slow, so I would like to take control of the positioning myself.
You can override the Components styles by adding a styles prop to your Select, targeting the component. React-Select uses emotionCSS for styling, rather than classes, so it can key off of state changes.
const styles = {
menuPortal: (base, state) => {
let overrideProps = {};
// do something here
return Object.assign(base, overrideProps);
}
};
<Select styles={styles} />
You can find additional info in the Styles section of the documentation.

Vue.js dynamic component reload

I have a vue.js app where I use the componenttag with :is="currentView" approach for changing the active view. I have a "state machine" that keeps track of valid transitions from a component to another component depending on some business logic. Sometimes this will tell the vue instance that the new value of currentViewis the same as the old one. If this happens, the component will not be reloaded. Is there any way I can force the component to reload even if the view is the same? That is I want the data to be reloaded, and the lifecycle hooks being executed.
You can add a unique key attribute to your component definition so that it correctly triggers all the component lifecycle methods.
Your component definition should look something like this:
<component :is="view" :key="unique_key"></component>
Helpful links:
Reloading a dynamic component
Vue 'key' api reference
This seems to do the trick, hower it causes the view to flash, is there a way I can avoid this?
const oldView = this.currentView
this.currentView = engine.advance()
if(this.currentView == oldView) {
//force reload!
this.currentView = ''
this.$nextTick(function () {
this.currentView = oldView
})
}