to update the data I need to reload the page, how can I avoid this? - react-redux

I have an application that saves the user in localStorage, I have checks for the existence of user on localStorage
componentDidMount(): void {
const {getNotes,} = this.props;
const userDataJSON = localStorage.getItem('userData');
if (userDataJSON) {
const {userID, sessionID,} = JSON.parse(userDataJSON);
return getNotes({sessionID, userID,});
}
}
but i have same checks in other blocks of code and i decide do it in utils
const userDataJSON = localStorage.getItem('userData');
export const userID = userDataJSON ? JSON.parse(userDataJSON).userID : null;
export const sessionID = userDataJSON ? JSON.parse(userDataJSON).sessionID : null;
export const username = userDataJSON ? JSON.parse(userDataJSON).username : '';
but data doesn't refresh when i logout and login in same session, i need reload page to correct work, how to refresh variables without reload page?
when i don't use utils all work good.

What's happening is that the code you are exporting on utils is only executed once (on initial load), because you are only storing the value in that moment, you could change those to be functions like this:
export const getUserData = () => {
const data = localStorage.getItem('userData');
return JSON.parse(data);
};
And you then when you use it, it will get the current value each time
const {userID, sessionID, username} = getUserData();
Another approach to avoid checking local storage each time you need the value could be to update the value on your state when modified and bring from local storage on initial mount, like this:
constructor(props) {
super(props);
this.state = getUserData();
}
updateUsername(username) {
this.setState({ username });
setUserData({ username }); // Assuming this one
}

Related

Remix: Generate links to all routes programatically

I'm a creating a blog using Remix.
Remix supports MDX as a route, which is perfect for me, as I can just write my blog posts as .mdx files and they'll naturally become routes.
However, if you access the index route - I would like to display the list of links to all the articles, (ideally sorted by creation date, or some kind of metadata in the .mdx file).
I can't see a natural way to do this in the Remix documentation.
Best solution I've got is that I would run a script as part of my build process, that the examines the routes/ folder and generates a TableOfContents.tsx file, which the index route can use.
Is there an out of the box solution?
The remix documentation does hint at this, but doesn't appear to provide a solid suggestion.
Clearly this is not a scalable solution for a blog with thousands of posts. Realistically speaking, writing is hard, so if your blog starts to suffer from too much content, that's an awesome problem to have. If you get to 100 posts (congratulations!), we suggest you rethink your strategy and turn your posts into data stored in a database so that you don't have to rebuild and redeploy your blog every time you fix a typo.
(Personally, I don't think redeploying each time you fix a typo is too much of a problem, it's more I don't want to have to manually add links everytime I add a new post).
For posterity, I'll post my current solution.
I run this script at build time, and it inspects all the blog posts and creates a <ListOfArticles> components.
import { readdir, writeFile, appendFile } from 'node:fs/promises';
const PATH_TO_ARTICLES_COMPONENT = "app/generated/ListOfArticles.tsx";
const PATH_TO_BLOG_POSTS = "app/routes/posts"
async function generateListOfArticles() {
try {
const files = await readdir(PATH_TO_BLOG_POSTS);
await writeFile(PATH_TO_ARTICLES_COMPONENT, `
export const ListOfArticles = () => {
return <ul>`);
for (const file of files) {
if (!file.endsWith(".mdx")) {
console.warn(`Found a non-mdx file in ${PATH_TO_BLOG_POSTS}: ${file}`)
}
else {
const fileName = file.split('.mdx')[0];
await appendFile(PATH_TO_ARTICLES_COMPONENT, `
<li>
<a
href="/posts/${fileName}"
>
${fileName} </a>
</li>
`)
}
}
appendFile(PATH_TO_ARTICLES_COMPONENT, `
</ul>
}
`)
} catch (err) {
throw err;
}
}
generateListOfArticles().then(() => {
console.info(`Successfully generated ${PATH_TO_ARTICLES_COMPONENT}`)
})
Is quite rudimentary - could be extended by inspecting the metadata section of the MDX file to get a proper title, doing sort order, etc.
This is not exactly what you were looking for but I used your solution as inspiration to figure out a solution to my own problem: Generating a type for routes so that the type checker can make sure there aren't broken links hanging around in the app.
Here's my script, generate-app-path-type.ts:
import { appendFile, readdir, writeFile } from "node:fs/promises";
const PATH_TO_APP_PATHS = "app/utils/app-path.generated.ts";
const PATHS_PATH = "app/routes";
async function collectFiles(currentPaths: string[], currentPath = "") {
const glob = await readdir(PATHS_PATH + currentPath, { withFileTypes: true });
for (const entry of glob) {
if (entry.isFile()) {
currentPaths.push(currentPath + "/" + entry.name);
} else if (entry.isDirectory()) {
await collectFiles(currentPaths, currentPath + "/" + entry.name);
}
}
}
// NOTE: the `withoutNamespacing` regex only removes top level namespacing like routes/__auth but
// wouldn't catch namespacing like routes/my/__new-namespace
function filenameToRoute(filename: string): string {
const withoutExtension = filename.split(/.tsx?$/)[0];
const withoutIndex = withoutExtension.split(/\/index$/)[0];
const withoutNamespacing = withoutIndex.replace(/^\/__\w+/, "");
// Return root path in the case of "index.tsx"
return withoutNamespacing || "/";
}
async function generateAppPathType() {
const filenames: string[] = [];
await collectFiles(filenames);
await writeFile(PATH_TO_APP_PATHS, `export type AppPath =\n`);
const remixRoutes = filenames.slice(0, filenames.length - 1).map(filenameToRoute);
// only unique routes, and alphabetized please
const routes = [...new Set(remixRoutes)].sort();
for (const route of routes) {
await appendFile(PATH_TO_APP_PATHS, ` | "${route}"\n`);
}
await appendFile(
PATH_TO_APP_PATHS,
` | "${filenameToRoute(filenames[filenames.length - 1])}";\n`,
);
}
generateAppPathType().then(() => {
// eslint-disable-next-line no-console
console.info(`Successfully generated ${PATH_TO_APP_PATHS}`);
});
The script generates a type like:
export type AppPath =
| "/"
| "/login";
I use this type with a utility file:
import type { AppPath } from "./app-path.generated";
import { SERVER_URL } from "./env";
export function p(path: AppPath): AppPath {
return path;
}
export function url(path: AppPath): string {
return new URL(path, SERVER_URL).toString();
}
Finally, whenever I need a path (like when I redirect a user), I can do this which is a little more typing than the string literal but is a confirmed URL according to the type checker:
p("/login") // or url("/login")

Subscribe to a doc using Svelte / RxJs / RxFire. How can I update the subscription

I use a derived store in the code below. It feels like a strange construct because I only use the derived construct for the dynamic $session dependency and to get the normData. But not with $norm. I use $norm only once to kick off the derived store.
Nevertheless it seem to work fine. But I have to renew the subscription if the $session changes. Is it possible to update the RxFire / RxJs subscription without unsubscribing first?
let normDocRef = null;
let normData = null;
let normSubscription = null;
const norm = derived(
session,
$session => {
normDocRef = db.doc(`uploads/${$session.a_id}_${$session.year}`);
// renew the subscription if $session changes
if (normSubscription)
normSubscription.unsubscribe();
normSubscription = doc(normDocRef).subscribe(snapshot => {
if (snapshot.exists) {
normData = snapshot.data();
} else {
normData = null;
};
});
},
);
$norm; // kick off the derived store to monitor $session
// show the data and updates
$: console.log(normData);
onDestroy(() => {
if (normSubscription) normSubscription.unsubscribe();
});
Update: I can use the set and return options of the derived store to change $norm in a real $norm Svelte store. Code below in my own answer.
But the real question is: Can I update a subscription. Change the subscription without the unsubscribe?
I already had the answer, but did not realize it.
Below the derived store code with the set() and return() options.
When the session changes the return() will unsubscribe automatically.
So still an unsubscribe and not an update ... but this feels good. Nice!
let normDocRef = null;
let normSubscription = null
const norm = derived(
session,
($session, set) => {
normDocRef = db.doc(`uploads/${$session.a_id}_${$session.year}`);
normSubscription = doc(normDocRef).subscribe(snapshot => {
if (snapshot.exists) {
set(snapshot.data());
} else {
set({}); // clear
};
});
return () => {
normSubscription.unsubscribe();
};
}, {} // initial value
);
$: console.log('$norm', $norm); // Now it is a real store
onDestroy(() => {
if (!normSubscription.closed) {
normSubscription.unsubscribe();
}
});
API docs derived store:
Derives a store from one or more other stores. Whenever those dependencies change (like the $session), the callback runs.
If you "return a function" from the callback, it will be called (before the callback) when a) the callback runs again (because the dependency changed), or b) ...
Ok, roughly get what you trying to describe over here.
You can actually use the reactive declaration to execute code when a variable / store changed.
In this case is to execute the resubscribe method:
let normDocRef = null;
let normData = null;
let normSubscription = null;
$: {
normDocRef = db.doc(`uploads/${$session.a_id}_${$session.year}`);
// renew the subscription if $session changes
if (normSubscription) {
normSubscription.unsubscribe();
normSubscription = doc(normDocRef).subscribe(snapshot => {
if (snapshot.exists) {
normData = snapshot.data();
} else {
normData = null;
};
});
}
}
onDestroy(() => {
if (normSubscription) normSubscription.unsubscribe();
});
The key here, is that when compiling this, Svelte knows that the block is depending on $session, so it will re-execute the code block whenever $session changed.
Should you want to refactor it out into another function, you need to make sure that Svelte knows that function depends on $session, ie:
$: resubscribe_norm($session);
Here, Svelte can tell that, if $session changed, need to call resubscribe_norm again.

How to identify an entity has quick create form enabled or not by Javascript?

I have some code below by which I am opening quick create form for entities if enabled but if not enabled I want to open in new window. I need to identify the entity has enabled quick create form or not on definition and how to do it by Javascript?
var entityFormOptions = {};
entityFormOptions["entityName"] = "contact";
entityFormOptions["useQuickCreateForm"] = true;
// will make it true if quick create form not enabled
entityFormOptions["openInNewWindow"] = false;
// Set default values for the Contact form
var formParameters = {};
// Open the form.
Xrm.Navigation.openForm(entityFormOptions, formParameters).then( function (success) { console.log(success); }, function (error) { console.log(error); });
I have found the solution, I can find IsQuickCreateEnabled through this api request:
[organization URI]/api/data/v9.0/EntityDefinitions

nativescript - how set global variables

I'm starting with nativescript with latest version.
I've finished tutorial on offical page, but now i have more questions than answers.
Can anybody tell me, what strategies can i use to set some variables, for example after succesfull login, how to set variable or even better, run some function that is doing checks globally, and not on every view or model file ?
I see that app.js is starting point for app, but looks like it cannot do any global checks ?
I think, second question is related :
Almost every model view file (file called as {viewname}).js is using:
var frameModule = require('ui/frame');
Is there a way to declare this once ? Or i have to run this every time when i need frame methods ? Or maybe if this is possible, lead to poor perforance?
Thanks for any advices.
NativeScript has a global Variable Scope.
To use it add global. in front of a variable name:
global.userName = "usernameXYZ";
To call it:
console.log("Username= "+global.userName);
Thanks Nikolay. Im using this pattern (maybe will help somebody)
register JS:
var page = require ("ui/core/view");
var frameModule = require('ui/frame');
var appSettings = require('application-settings');
exports.loaded = function(args) {
page = args.object;
};
exports.register = function() {
var userName = page.getViewById('userName');
var userPass = page.getViewById('userPass');
if (userName.text != '' && userPass.text != '') {
var name = userName.text;
var pass = userPass.text;
appSettings.setString('name', name);
appSettings.setString('pass', pass);
appSettings.setBoolean('auth', true);
var topmost = frameModule.topmost();
var navigationEntry = {
moduleName: "main-page",
context: { info: "something you want to pass to your page" },
animated: true,
transition: "curlDown",
clearHistory: true
};
topmost.navigate(navigationEntry);
} else {
alert('Please, fill form');
}
};
i use model as a global like this, in model.js (or whatever you want to call it)
`
var viewModel=require("data/observable");
var model= new viewModel.Observable();
module.exports= model;`
Then say in your login.js
` const model=require('./model')
const password = //a password from page
const userName = //a name from page
//after some logic to verify credentials set them accordingly as below
model.password=password
model.userName=userName
`
now if say you are navigated to home.js or any page for that matter, just call it whenever required like so const pass=model.password,name=model.userName all with courtesy from 'model.js'

Multiple page objects in one test case

So far the example is only using one page object in a test case. Can we have multiple page objects in a test case?
Imagine that I have a test case which required to login, and then followed by creating an user.
So I have two page objects, one for login page and another for user page. We will use the page objects like this?
module.exports = {
'login': function (browser) {
var login = browser.page.login();
login.navigate()
.click('#submit');
},
'create user': function (browser) {
var users = browser.page.users();
users.navigate()
.click('#submit')
.end();
}
}
My code would be like :
module.exports = {
'create user': function (browser) {
const pages = browser.page,
login = pages.login(),
userPage = pages.users();
login.navigate()
.setValue('#username','myuser')
.setValue('#pass','mypass')
.click('#submit',function(){
users.navigate()
.click('#submit')
.end();
})
}
}

Resources