What does the "Persist state history" button do? - redux

What does this "Persist state history" button do in redux devtools? Why would the state not always be "persisted"?

This is pin button UI code:
{features.persist &&
<Button
Icon={PersistIcon}
onClick={togglePersist}
>Persist</Button>
}
See v2.15.0/src/app/containers/App.js#L95
features doc:
const composeEnhancers = composeWithDevTools({
features: {
pause: true, // start/pause recording of dispatched actions
lock: true, // lock/unlock dispatching actions and side effects
persist: true, // persist states on page reloading
export: true, // export history of actions in a file
import: 'custom', // import history of actions from a file
jump: true, // jump back and forth (time travelling)
skip: true, // skip (cancel) actions
reorder: true, // drag and drop actions in the history list
dispatch: true, // dispatch custom actions or action creators
test: true // generate tests for the selected actions
},
// other options like actionSanitizer, stateSanitizer
});
features.persist means persist states on page reloading.

Related

VurRouter transitions on more than one router

I prepared a boiled-down example on stackblitz:
https://stackblitz.com/edit/quasarframework-vy4eiw?file=README.md
The problem I try to resolve is this:
A quasar 2 app build with vite and vue 3 (and GSAP) uses layouts
Currently there are 2 layouts: StartpageLayout for the startpage at route ´/´and MainpageLayout for all the other pages at route ´/main´ and any children of it (/main/:child´`)
The MainpageLayout also contains the navigation menu
The navigation menu should be created (later on with an animation) when any route starting with ´/main´ is hit and destroyed, when there is a change to any other route
While navigating through any ´/main[/:child]´ route, the nav menu shall remain "stable" (not rebuild or anything like that)
The app uses 2 router-views for this, one in App.vue, one in MainLayout.vue. Changes between those states should mainly be handled in onBeforeRouteLeave and onBeforeRouteUpdate
To check, whether the app is in a "layout context", the routes have a meta.layoutKey, which is used in router guards to check, whether sth changed or not:
// Example: src/layouts/MainLayout.vue
onBeforeRouteUpdate((to, from, next) => {
console.log('%cMAIN_LAYOUT: onBeforeRouteUpdate invoked', consColRouter);
// compare meta.layoutKeys of routes
if (from.meta.layoutKey !== to.meta.layoutKey) {
console.warn(' YES, invoke router guard onBeforeRouteUpdate - animate!');
next() // this would be actually be called form an onComplete animation callback
} else {
console.log(' NOPE, do not invoke router guard onBeforeRouteUpdate');
next() // invoked as written
}
})
A pinia store manages state that (should) remember(s) activateMenu:
// Pinia store "pageTransitions.js" (composition API)
import { ref, reactive, computed } from 'vue'
import { defineStore } from 'pinia'
export const usePageTransitionsStore = defineStore('pageTransitions', () => {
// Pinia state in composition API
const pageTransitions = ref({
parent: false,
activateMenu: false
})
const setPageTransitions = (level, bool) => {
switch(level) {
case 'parent': {
pageTransitions.value.parent = bool
break
}
default: { console.log('default... must never happen!'); }
}
}
const setActivateMenu = (bool) => {
pageTransitions.value.activateMenu = bool
}
return {
pageTransitions,
setPageTransitions,
setActivateMenu
}
})
If store.pageTransitions.activateMenu is true, show the menu, if false, remove it. It is imported in MainLayout in order to use the activateMenu constant to manage the state of the nav menu. The onMount method sets this store variable to true. And it should be set to false in a ònBeforeRouteLeave`... (not yet implemented)
While the change from the startpage at ´/´to the MainPage at ´/main´ and vice versa works fine (even with animation, due to the store variable store.pageTransitions.parent), I keep having troubles with changes from ´/main´ to any child route ´/main/:child´ and vice versa. E.g. when the app is at /main and the user clicks on ´items 101´, the whole MainLayout is reloaded - also App.vue runs through its onAppear hooks again (see console) – and the nav is set to false again.
The goal is to not influence the MainLayout not its nested nav menu at all.
I wonder, why those reloads happen? MainLayout's onBeforeRoute checks against meta.layoutKey which does not change. But then I also observe that the pinia store gets loaded again, and the actiavteMenu var is set up false again...
Does anybody see my error(s)?

Redux: right way to handle concurrent requests

I just can't grasp the core concept I guess.
I have a page with contact list. But user can visit the page with contactID in URL, and in this case I should open a popup window with contact info. So, what am I doing:
My store looks like:
{
isRequested: boolean, // is initial loading done
isLoading: boolean, // is currently loading,
contacts: {[ key: number ]: IContact}
// where key is contact ID and IContact is generic a model
}
Open the page and start loading contacts list.
Without waiting for contacts I'm openinig the popup window.
So, how should I handle this situation? The only thing that come to my mind is to create dummy contact if it's not exist in store. Something like:
{
...,
contacts: {
1: {
isRequested: false,
isLoading: true,
isCorrupted: false // in case of bad ID
}
}
}
And subscribe to state change in popup window, to wait for this contact isRequested to change.
And when all contacts loaded - add all contacts without this one, or merge them.
Is it right approach, or is it a better solution do exists?
I would keep contacts empty and simply display a loading screen until the full list is loaded.
So your state looks like
{
isLoading: boolean, // is currently loading,
contacts: {[ key: number ]: IContact}
}
and the initial state
{
isLoading: false,
contacts: {}
}
In your view:
If contacts is an empty object, display nothing.
On first load (componentWillMount in React), load the list of contacts and set isLoading to true.
When isLoading is true, display a loading screen.
When the list is ready, display your information

Redux - conditional dispatching

Classic problem - I want to validate a form before submitting it.
My submit button triggers an action (simple dispatch or thunk). If the form is valid, submit it - else, trigger an error message.
The simple solution: dispatch an action e.g. VALIDATE_AND_SUBMIT, which in the reducer will validate the form, and submit or set an error state.
I feel like these are two different actions: VALIDATE
{
type: "VALIDATE",
formData
}
This should validate and set errors.
{
type: "SUBMIT",
// get form data and errors from state
}
SUBMIT - should submit provided there's no error state.
Even if I use redux-thunk, I can't get feedback from the first VALIDATE action. Is my thought process anti-pattern here? How can I validate before submitting a form?
I think part of your issue is think of actions as something that is happening rather than something has caused the state to change.
"Validate" is not an action. "Validation failed" is the action.
"Submitting" the data is not an action. "Data was submitted" is the action.
So if you structure your thunk with that in mind, for example:
export const validateAndSubmit = () => {
return (dispatch, getState) => {
let formData = getState().formData
if (isValid(formData)) {
dispatch({type: "VALIDATION_PASSED"})
dispatch({type: "SUBMISSION_STARTED"})
submit(formData)
.then(() => dispatch({type: "SUBMITTED" /* additional data */}))
.catch((e) => dispatch({type: "SUBMISSION_FAILED", e}))
}
else {
dispatch({type: "VALIDATION_FAILED" /* additional data */})
}
}
}
why don't validate form before dispatching, and dispatch the appropriate action? or you can use redux middle-ware click here
Classical and whole solution:
Hold the entire form in your state, for example:
{
valid: false,
submitAttempted: false,
username: '',
email: '',
}
Add onInput event to your form inputs and track their state validating on every change.
So when the user submits, you can check the valid state and react.
redux-form
was built just for that.
Assuming you don't want to hold the entire form state
In this case, you'll have to validate and then submit.
Here's an example assuming using react-redux with mapStateToProps and mapDispatchToProps
// action in component
submitButtonAction() {
props.validate();
if(props.formValid) props.submitForm();
}
// JSX
<FormSubmitButton onClick={this.submitButtonAction} />

Document event listener not unbinding in Tracker.autorun() or when Template Destroyed

after dismissing a menu by clicking anywhere else on the page, I noticed when console logging from the callback that document.body will 'remember' the previous event listeners after I move on to another template and trigger the same behavior. The callback is dismissMenu found below. For example...
First time:
hello
Second time:
hello
hello
Etc....
What am I doing wrong here?
Thanks!
// named callback to dismiss menu so I can unbind later using $.off()
const dismissMenu = function() {
console.log('hello')
$('.js-dd').addClass('js-hidden')
Session.set('menuOpen', false)
$(document.body).off('click', dismissMenu)
}
Template.app_bar_expanded.onCreated(function() {
this.stackId = FlowRouter.getParam('_id')
// opening the menu will trigger the Session var to 'true'
Tracker.autorun(function() {
const menuIsOpen = Session.get('menuOpen')
if( menuIsOpen ) {
$(document.body).on('click', dismissMenu)
}
})
})
// Stem the event bleeding some...
// TODO get this .off() to actually work as you would expect.
Template.app_bar_expanded.onDestroyed(function(){
$(document.body).off('click', dismissMenu)
})

browser-sync for SPA: how to sync document fragments: #/contact

I'm developing a SPA (Single Page Application) and use grunt-browsers-sync.
All browser-sync features seem to work: CSS injection, scrolling and form synchronization.
It's a SPA so no navigation to other pages in done. Navigation is done via routes in the document fragment (I use the SammyJs library for this)
mysite.com/#/home
mysite.com/#/contact
mysite.com/#/...
It seems BrowserSync doesn't synchronizes document fragments.
I think it's because document fragments are handled by the browser and not requested at the BrowserSync server/proxy.
Is there some way to make the scenario work?
PS: I have a javascript callback when navigating, which I can use to send the new url to BrowserSync at development (if BrowserSync supports something like that)
I also tried using browser-sync for a single-page backbone application.
Routes changes are basically triggered on clicking anchors. Unfortunately, browser-sync doesn't play well with events having stopPropagation & hence, the click wasn't triggered in other browsers and routes were synced.
Since then I've forked and fixed this plus other issues namely event syncing for mouseup, mousedown, keyup, keydown and contenteditable div.
The pull-request is still pending though, so meanwhile you can use browser-sync-client from https://github.com/nitinsurana/browser-sync-client
You'll need to have configuration as follows for the fixes to take effect . Notice the capture, contenteditable, mouseup and other config options not present in browser-sync
var bs = require("browser-sync").create();
var client = require("./");
client["plugin:name"] = "client:script";
bs.use(client);
bs.init({
//server: {
// baseDir: ["test/fixtures"]
//},
proxy: 'http://localhost:8080',
open: false,
minify: false,
snippetOptions: {
rule: {
//match: /SHNAE/,
match: /<\/head>/i,
fn: function (snippet) {
return snippet + "\n</head>";
}
}
},
clientEvents: [
"scroll",
"input:text",
"input:toggles",
"input:keydown",
"input:keypress",
"form:submit",
"form:reset",
"click",
"contenteditable:input",
"mouseup",
"mousedown",
"select:change"
],
ghostMode: {
clicks: true,
scroll: true,
forms: {
submit: true,
inputs: true,
toggles: true,
keypress: true,
keydown: true,
contenteditable: true,
change: true
},
mouseup: true,
mousedown: true
},
capture:true
});

Resources