In ReactJS, is there a way to constantly check to see if the token saved in the localstorage has expired? If it has expired, would like to remove the token.
Came across the following but doesn't it only get triggered when the page gets reloaded?:
window.onbeforeunload = function() {
//remove token
return '';
}
The following assumes you are using redux... you can create a middleware that will trigger an action when a token expires.. this will allow for you to handle a reducer downstream. The redux approach is mainly because Redux is currently the most popular state management solution used with React.
// export the action type used as a const, so it can be imported for
// the reducer to listen for... The export const/key is more convenient
// then you can use a more namespaced string value to help prevent collisions
export const TOKEN_EXPIRED = 'tokenExpiredMiddleware_TokenExpired';
// redux middleware pattern (store) => (next) => (action) => result;
export default function expiredTokenMiddleware(store) {
// here we only care about a handle to the store for store.dispatch
// start checking every 15 seconds, probably want this configurable
setInterval(checkToken.bind(null, store), 15000);
// return a pass-through for the plugin, so other plugins work
return (next) => (action) => next(action);
}
// export checkToken for testing... etc
// should probably be a separate module
export function checkToken(store) {
var tokenId = ''; // TODO: Identify token
var isExpired = true; // TODO: replace with function to get current state
if (isExpired) {
store.dispatch({
type: TOKEN_EXPIRED,
payload: { tokenId },
});
}
};
Then, when you do your createStore, you can just add this middleware which will emit the appropriate action, and you can handle it in your appropriate reducer... I do something similar for the window's resize/scroll events so that my size/position is always set.
This is using ES6+ syntax, since you're using React I think that is a fair assumption
Since you cannot use code running on the user's machine for security relevant use cases anyway, why not check the token only when it is used?
At some point you most likely load the token from local storage and use it to e.g. authenticate the session. Why not first check its validity then before using it?
This saves you the trouble of having an ongoing activity that checks the token, bundles related functionality and most likely reduces the complexity of your code.
After all, the token won't do any harm by just being stored in the browser's storage without being used, would it?
Related
I have a store that fetches data once in a while – according to user's actions. This is a store because its data is used globally and mainly all components needs the latest data available.
But, for one specific component, I only need the first data loaded.
For this component, there is no reason to keep a subscribe() function running after the first fetch. So, how can I stop this subscribe function?
The only example in Svelte doc's uses onDestroy(), but I need to manually stop this subscribe().
I tried with a simple "count" (if count > 1, unsubscribe), but it doesn't work.
import user from './store'
let usersLoaded = 0
const unsubscribe = user.subscribe(async (data) => {
if(data.first_name !== null) {
usersLoaded = usersLoaded + 1
}
if(usersLoaded > 1) {
unsubscribe;
}
});
Here's a full working REPL:
→ https://svelte.dev/repl/95277204f8714b4b8d7f72b51da45e67?version=3.35.0
You might try Svelte's get. A subscription is meant for situations where you need to react to changes; it's a long-term relationship. If you just need the current value of the store, get is the way to go.
Occasionally, you may need to retrieve the value of a store to which you're not subscribed. get allows you to do so.
import { get } from 'svelte/store';
const value = get(store);
I had to use unsubscribe() instead of unsubscribe 🤡
Here's the final working REPL with some improvements:
https://svelte.dev/repl/95277204f8714b4b8d7f72b51da45e67?version=3.35.0
You can use auto subscribe: $user which will also auto unsubscribe.
Some more details in the docs.
Example:
let user1 = null;
$: if ($user?.first_name && !user1) {
user1 = $user.first_name;
console.log('first user', $user.first_name);
}
And you do not really need a writable store here. You can use a readable and use the set method to handle the fetch.
Something like:
const user = readable(defaultUser, set => {
.... fetch the data ....
.... set(data)
}
By the way: This is already async code and you can use set(data) to store the fetch result.
Updated: 04 Jan 2023
Best way to unsubscribe is using onDestroy Svelte's hook
import { onDestroy } from "svelte"
const subcriber = page.subscribe((newPage) => handleChangePage(newPage.params.id))
onDestroy(subcriber)
I'm building a Single App that will do SSR (server side rendering) and I'm using React + Redux.
I've just started to implement Redux in this app. It was previously built app using only React's useState, useContext etc.
The fact is that sometimes I need my app code to be aware of the environment that it's running, either ON_CLIENT or ON_SERVER, to skip some window.something statement, for example.
Before Redux, I was doing the following:
index.js (this could be the index.js of my client bundle or my server bundle)
ReactDOM.render(
<App
ON_SERVER={false} // THIS IS TRUE ON SERVER CODE
ON_CLIENT={true} // THIS IS TRUE ON CLIENT CODE
... other stuff
/>
,document.getElementById('root')
);
App.js
...
const environment = {
ON_SERVER: props.ON_SERVER,
ON_CLIENT: props.ON_CLIENT
}
...
// PROVIDING IT USING REACT CONTEXT
return (
<EnvironmentContext.Provider value={environment}>
<MyComponents/>
</EnvironmentContext.Provider>
);
And then, inside some component, I can do this pattern:
SomeComponent.js
const {ON_CLIENT} = useContext(EnvironmentContext);
ON_CLIENT && window.something;
And I want to improve this pattern with Redux.
I want to keep this in the Redux store, so I can get rid of the EnvironmentContext and access it with:
const {ON_CLIENT} = useSelector((state) => state.environment);
So I've thought of doing:
index.js
const store = createStore(rootReducer, {
environment: {
ON_CLIENT: true, // THIS IS TRUE ON CLIENT CODE
ON_SERVER: false // THIS IS TRUE ON SERVER CODE
}
});
But since I don't have a corresponding reducer for this piece of state (environment), I got this error msg:
redux.js:319 Unexpected key "environment" found in preloadedState argument passed to createStore. Expected to find one of the known reducer keys instead: "auth", "appVersion", "siteData". Unexpected keys will be ignored.
NOTE: auth, appVersion and siteData are pieces of state which I have corresponding reducers for.
Here is my rootReducer:
const rootReducer = combineReducers({
auth: updateAuth,
appVersion: updateClientVersion,
siteData: updateSiteData
});
QUESTION
Can I have some piece of state that will not change, and therefore is not handled by any reducer? Or in this case I do need to set up some dummy reducer just to always return that same state? PS: It does the trick, but it feels wrong, though.
// NOTE: I will always preload this state, in the `createStore` call, so the state will never be undefined.
function returnEnvironment(state={}, action) {
return state;
}
const rootReducer = combineReducers({
auth: updateAuth,
appVersion: updateClientVersion,
siteData: updateSiteData,
environment: returnEnvironment
});
Does anybody have a better alternative to this?
I've looked at this discussion: https://github.com/reduxjs/redux/issues/1457
There are some suggestions to populate the global object, but I'd rather keep it all inside React and Redux.
PS: Sorry for the long question, but I wanted to make my use case as clear as I could, so somebody might have a better pattern.
Redux is designed for data centralisation and management such as loading some dynamic data changing it during the application lifetime and so on.Since you are not going to change that values because your application can’t run in both environments on a same time or switch between them that mean you don’t need to change them during application lifetime and if some variable should not change value it should be declared as a CONSTANT. So declare it as a constant and important whenever you need to access it.
what i want to do is dispatch an action in my set interval function and not in get initial props and save my data in store and how to get that data back from store in react app it was simple just import action form action file and call like this this.props.actionName() but how do i do this in next and to get data from store we map state to props how can it be done in next thanks here my function which i want to implement in
this.fetchCryptoData().then(data => {
var Keys = Object.keys(data.DISPLAY);
this.setState(
{
crypto_head_coins: Keys
},
() => {
// // this.props.update_array([]); // update_array() is my action i haven't imported it
let rate_updated = [true, true, true, true]; // i want my store updated_array data here
for (let i = 0; i < this.state.crypto_head_coins.length; i++) {
//my code here
// this.props.store.dispatch(update_rate_array(rate_updated)) //it says cant read property
// of dispatch of undefined
// i want to dispatch my action here not in getinitialprops
this.setState({ rate_updated });
}
);
});
I use NextJS sometimes, It is the same as a Create-React-App essentially.
I just noticed your question does not include 'React-Redux', You will need to install/save 'React-Redux' and 'Redux' to use connect/dispatch, etc. I have a sample boilerplate on Github.
Another missing piece for converting this into an action.. is perhaps redux-thunk, to handle promises.(Try without it first.)
More information on redux-thunk here.
https://github.com/reduxjs/redux-thunk
You are setting state twice(once in the callback of another), which is going to cause multiple re-renders. (Unless ShouldComponentUpdate is implemented) Might want to re-consider this design.
Implement your MapDispatch to Props
After doing so you can simplify the line calling it, like the below using destructing.
// this.props.store.dispatch(update_rate_array(rate_updated)) //it says cant read property
let update_rate_array = {this.props}
update_rate_array(rate_updated)
You should implement your MapDispatchToProps removing some complexity in the naming and calling.
I have uploaded some simple examples to Github, and there is also an identical related CodeSandbox.
To receive your updated information from State, use MapStateToProps.
Example here.
We are developing APIs using Apollo GraphQL. We use the out of the box caching solution that Apollo provides (KeyValueCache using a Redis datastore).
When a request query arrives, why does ApolloServer check the cache first before it does anything else?
Is there any way to insert logic before the cache is touched? For example, we want to do some authentication and permissions checking before Apollo checks the cache.
(Yes, there are directives but we find Public/Private scope and maxAge insufficient for our needs.)
The code and explanation below flags a few different approaches for you to explore -- hopefully one will suit your needs (I am assuming you know you can control whether Apollo looks to cache first by fetchPolicy - although I discuss this briefly below). First, consider using a HOC that checks permissions and authentication prior to returning the passed Component. The permissions/auth data can be passed as props should the passed Component be rendered.
withUserData = Component => {
const { isValidated, userData } = checkAuthAndPermissions(); // Modify for your implementation
if (!isValidated) return null;
return <Component userData={userData} />
}
You can thereafter wrap any Component that needs to make the auth/permission check with the HOC, as shown below. As also shown below, Apollo provides the opportunity to skip the query altogether looking to props or other logic, if that is something you may consider. Finally, through the option prop, you have the ability to set the fetchPolicy, which could be dynamically based on a permission check or props. With this fetchPolicy you could avoid looking to cache if that is an objective.
const ComponentWithApollo = graphql(YOUR_QUERY, {
skip: props => { /* consider permissions/auth here, skip if needed */ },
options: props => {
const fetchPolicy = determineFetchPolicyFromAuthOrPermissions();
return { fetchPolicy };
},
props: ({ data }) => data
})(YourComponent);
withUserData(ComponentWithApollo);
I have 3 general questions about redux and isomorphic application:
What is the best way to share 'runtime' data between client and server?
For instance, when the user logged in a distant API, I store the session object in cookies. In that way, next time the client requests my front-end, the front-end server can read the cookies and initialize the redux store with it's previous session. The downside of this is that the client HAS to validate/invalidate the session on boot (eg in componentDidMount of the root component).
Should I request the session server side rather than read it from cookies?
Where should I execute the operation of cookie storing, in action creators or in reducers? Should I store the cookie in my reducer that handle the user session?
Where should I execute the operation of redirect the user (via react-router)? I mean when my user is successfully logged in, from where should I dispatch the redirect action (from the loginActionCreator once the login promise is resolved?, somewhere else? )
Thanks in advance.
I managed to get a really neat app structure.
Here's what I found for each questions:
I only share between my client and front-end server the API server token via cookies. Each time the client request the site. The front-end server calls the API server to validate the session. If these servers are on the same network it's really fast (< 5ms). I also prefetch some useful data for the client on the server before the initial render. I manage to get my application loaded and ready (javascript loaded) in the client in 600ms. It is pretty decent.
The action of storing the cookie is in my actions creators. As Ethan Clark said, we must keep reducers pure. It's much more easier to test.
I still dispatch the redirect in my signin creator once the user is authenticated. I guess it's easier to test than to dispatch the action after the promise resolution in component or elsewhere.
In fact, keeping this in mind allows us to have an app really easy to test (expect for the actions creator where you must have ton of spies).
Hope it will help someone.
Thanks for participating.
Question 2: you should execute cookie storing in your action creator. Reducers must remain pure functions.
I'm really sorry that I don't know the answers to 1 & 3, but I hope that this helps!
You should probably break your questions up into three different stack overflow questions since they're all a little different.
I agree with Ethan, your reducers should be pure with no side effects. That's the goal (aka best practice) anyway. However, Ben Nadel has been exploring questions along these lines and suggests creating a workflow layer to manage business logic rather than placing that burden on the store. You should check out his Managing Locally Cached Data with Redux in AngularJS article for more information about that.
Cookies are synchronous - you can either hydrate and subscribe to your store or make a meta reducer which wraps around the reducer before it is added to createStore. Here's quick example of both below:
//first option
const Cookie = require('js-cookie');
const loadState = (key) => Cookie.getJSON(key);
const saveState = (nextState, key) => Cookie.set(key, nextState);
const persistedState = loadState('todos');
const store = createStore(
todoApp,
persistedState
);
store.subscribe(throttle(() => {
saveState({
todos: store.getState().todos,
}, 'todos');
}, 1000));
//second option - meta reducer
// usage
const Cookie = require('js-cookie');
export function cookieMeta (
key: string,
reducer: any,
expiry: Date | number = 365,
path: string = '/',
domain: string = window.location.hostname): Function {
return function(state: any, action: any): any {
let nextState = reducer(state, action);
let cookieState = Cookie.getJSON(key);
if (action.type.includes('DELETE')) {
Cookie.remove(key);
} else if (!nextState && cookieState || action.type === '##redux/INIT') {
nextState = cookieState;
} else if (nextState && nextState !== cookieState) {
Cookie.set(key, nextState, { expires: expiry, path: path, domain: domain, secure: process.env.local });
}
return nextState;
};
};
// how to implement the meta reducer
import { todos } from './todos';
import { cookieMeta } from './middleware/cookieMeta';
export function TODOS_REDUCER (state: any, action: any) {
return cookieMeta('todos', todos)(state, action);
}
export const todoApp = combineReducers({ todos: TODOS_REDUCER })