I have some general questions which are bothering me regarding the context API and local storage.
When to use local storage?, when to use the Context API and when would I use both?
I know that to persist data after the refresh I need something like local storage or session storage, so do I ditch the context API entirely and just store everything in the local storage? this way I can not only store data but also keep it on refresh? some insight would be really helpful.
What are some pros and cons?
Context API vs Local storage is apples vs oranges comparison.
Context API is meant for sharing state in the component tree.
Context provides a way to pass data through the component tree without having to pass props down manually at every level.
Local Storage is for storing data between sessions.
The read-only localStorage property allows you to access a Storage object for the Document's origin; the stored data is saved across browser sessions.
The right comparison might be Local Storage vs Cookies, Context API vs State-Management Library (not really, since Context is not a state management tool).
While you can store everything on the local storage (although it's not scalable and maintainable) it won't be useful.
It won't be useful because you can't notify your components on state change, you need to use any React's API for it.
Usually local storage is used for session features like saving user settings, favorite themes, auth tokens, etc.
And usually, you read once from local storage on application start, and use a custom hook to update its keys on related data change.
Here is a helpful recipe for useLocalStorage custom hook:
function useLocalStorage(key, initialValue) {
// State to store our value
// Pass initial state function to useState so logic is only executed once
const [storedValue, setStoredValue] = useState(() => {
try {
// Get from local storage by key
const item = window.localStorage.getItem(key);
// Parse stored json or if none return initialValue
return item ? JSON.parse(item) : initialValue;
} catch (error) {
// If error also return initialValue
console.log(error);
return initialValue;
}
});
// Return a wrapped version of useState's setter function that ...
// ... persists the new value to localStorage.
const setValue = value => {
try {
// Allow value to be a function so we have same API as useState
const valueToStore =
value instanceof Function ? value(storedValue) : value;
// Save state
setStoredValue(valueToStore);
// Save to local storage
window.localStorage.setItem(key, JSON.stringify(valueToStore));
} catch (error) {
// A more advanced implementation would handle the error case
console.log(error);
}
};
return [storedValue, setValue];
}
Related
Hi I am trying to get the value from local storage by using observable but it is not working.
My previous code was:
const isManager= JSON.parse(localStorage.getItem('isManager'));
console.log('isManager', isManager);
this.doNavigation(isManager);
But I change it in to this for continously reading value:
of(JSON.parse(localStorage.getItem('isManager'))).pipe(
takeWhile(() => this.alive),
map((isManager: boolean) => {
console.log('isManager', isManager);
this.doNavigation(isManager);
})
);
The problem is that it is not going inside the map body and there is a value present in local stroage regarding this field.
Why not use
const isManager = localStorage.getItem('isManager') === 'true';
// You need to convert the string stored in local storage to a boolean.
if (isManager) {
this.doNavigation(isManager);
}
What is the point of using of to create an observable that is mapped to undefined? Very smelly code.
I'm not sure there is an option to continuously read value from local storage, for that to work, you'd need to be able to subscribe to local storage item's value and that's just not possible.
What you've done in your solution is created Observable containing only one value (current local storage item's value), so after you subscribe to that method you'd only get current value, but it wouldn't change no matter if local storage item's value changes.
Since there is no direct way to subscribe to local storage, you can use service with BehaviourSubject which will constantly have stored the newest local storage item's value. That way wherever you subscribe to BehaviourSubject all dependent elements will always get the newest value from stream.
This is what the service which handles local storage should look like:
#Injectable({providedIn: 'root'})
export class LocalStorageService {
private isManager$ = new BehaviorSubject<boolean>(false);
constructor(){
// get current value from local storage
isManager = localStorage.getItem('isManager');
// set stream's last value to local storage item's value
// this way when service is initialized, isManager$'s value will be set to current local storage value
this.isManager$.next(isManager as boolean);
}
setIsManager(value):void {
// sets isManager$'s value to new value passed as argument
// after this line is executed, value of all subscriptions will change to the latest value
this.isManager$.next(value);
// sets local storage's value to new value
// in case user reloads page, constructor will set isManager$ to last available value
localStorage.setItem('isManager', value);
}
getIsManager(): Observable<boolean> {
// returns stream of values, which means, after you've subscribed, you'll always get the latest value from stream
return this.isManager$;
}
}
To learn more about BehaviourSubject check out RxJS documentation for BehaviourSubject. You can also watch this video which gives really good explanation of basics of BehaviourSubject and how to use them.
How do you access a session in an endpoint in sveltekit? I've tried this but no luck:
import { get } from 'svelte/store';
import { getStores} from "$app/stores";
function getUser() { // <- call this at component initialization
const { session } = getStores();
return {
current: () => get(session).user
}
}
The session store is being populated in src/hooks.js, the normal flow to do so is
in handle, add some data to event.locals.
in getSession, use event.locals to create a session object.
This session object is available in the client as the session store, and on during ssr if you use the load functions, but it is not available in endpoints.
What is available though in the endpoint is the locals variable, that is originally passed to the getSession function, so you can read that one.
export async function get({ locals }) {
// code goes here
}
Just be aware that this means there is no synchronization between locals and the client side session, if you add something to the session it will not be available to the endpoint. To handle this you would have to for example add new data to the cookie and parse this in the handle function.
The session store only works inside svelte components, (it uses context under the hood) this provides isolation between users.
You can import the getSession from src/hooks.js and pass the event to reuse the logic that extracts session data from the request.
I have some data from a function in my app.component that I would like to store and use in other components. How can I achieve this?
Here is the function:
private async setupLocalUser(): Promise<boolean> {
try {
this.token = await jwtDecode(this.oauthService.getAccessToken());
//add user
this.userId = await this.userService.AddUser(this.token.email);
//add user session
this.userSessionId = await this.userSessionService.AddUserSession(this.token.email);
return true;
}
catch (Error) {
this.errorMessage = Error;
return false;
}
this.userSessionId is what I'd like to store for use as a parameter in different functions that are in other components. How can I achieve this?
Take a look at this post, I'm sure it'll be useful for your problem. As they say there, you could use a service or sessionStorage (more info here) to store the data so it can be accessed across the application.
I hope this helps you!
There is number of ways by using you can store/transfer data across the components. It totally depends on your situation/requirement. For example, you can use
Service's
web storage (Localstorage/ SessionStorage/ Cookies etc)
Global Variables
Variables as behaviour subject.
But I would suggest if the value is confidential you should store in a Global variable or using services. By doing so it's a bit hard for anyone to fetch data from the production build.
Let me know if you still have any query.
My understanding of state variables in React is that only data that changes as a result of user interaction or asynchronous data flow should be stored in state. What if I need to request list data from several APIs and then combine these to make a new master list to serve as a constant source of truth that may need to be used by various component methods during phases of user interaction? What is the correct place to store unchanging data like this? It's tempting to put this data in state for easy access but that doesn't seem right. Thanks.
You don't need to use redux, in this case. It's OK to save your API data call to state, even though the data won't change. If you don't want to save it to state you can save it to a static or global variable.
class App extends React.Component {
API_DATA = []; // save data here
state = {
api_data: [] // it's ok too
}
componentDidMount = () => {
// call to api
// save data to API_DATA
this.API_DATA = data;
}
}
This is where something like React hooks or Redux would come in handy. If you're unfamiliar, both can be used to globally store state. Redux would be better for high frequency changes but it sounds like hooks would be best for your situation.
There are plenty of tutorials out there for this on Youtube, the React subreddit, etc.
I just exported a few things from my store to the localStorage (actual logged in user) so the user should be able to open a second tab without login again.
For putting data in the localStorage i used a Redux-Middleware (if action is login I put the login data in the localStorage).
export const storageMiddleware = () => next => action => {
console.log("Share Middleware...");
console.log(action);
switch(action.type){
case 'LOGIN':
localStorage.setItem('login-token', action.token);
localStorage.setItem('login-token-expiration', action.expiration);
break;
default:
break;
}
next(action);
};
Now my question:
Is there a simple way to get the data from the localStorage?
Like a read middleware where I call localStorage.getItem instead of returning the value from the redux store?
Or do I have to change every component which uses the login-data to access the localStorage instead of the redux store.
Now I access the token via mapStateToProps-method.
One of simple way to do this is the use redux-persist, it persist the redux state to local storage. Also there is option to blacklist or white-list the specific reducers. By using this package, we don't have to manually handle local storage as the redux store updates