Simple way to access localStorage instead of redux-store - javascript

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

Related

Access state in one redux saga file from other redux saga file

I am using react redux saga to implement something in a calendar application. This calendar application has multiple settings/params. One of them is the currentAccountId, which is in the initalState of a redux file and I want to render it dynamically.
const initialState = {
currentAccountId: ''
};
However, in order to do this, I need access to the state object on a different redux saga file. How do I access this? Because when I try to do something along the lines of const state = yield select(); outside of a generator function to access state, it tells me something like not accessible outside of a generator function. Is there some way I can access state?

Add to state from Redux middleware

I've created a redux middleware used for calling my API.
For better debugging I've also implemented status for each of the objects in my state.
Now I have to do this in every case of all reducers:
case SET_USER:
return {
...state,
workspaces: action.response.data.workspaces,
status: action.status, <--
}
Which is pretty repetetive. Is there any way I can change the state (at the correct place) each time automatically?
I do have access to the action type, maybe I could use that somehow?

React Context Api vs Local Storage

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];
}

React Redux: How to share data between reducers?

I have a basic todo list. The todo input field for the todo list has an onChange event that triggers an action and sends event.target.value to a reducer and stores each character the users types to a property of the store object.
When the user submits the form I want to get the data that was previously stored via the onChange event, then I want to place it on a new property on the store object.
How do I get data that was previously entered from the store and pull it into a different reducer?
In all the examples I've seen, reducers start with an "initial state". I don't want that, I want the previous state that the user entered.
Below is a CodeSandbox version of the code (for some reason the orange-beige tab to the right needs to be switch to the left to blue for it to render the form. If you don't do that it won't work).
https://codesandbox.io/s/pwqlmp357j
Ask yourself whether you've structured your reducers correctly. If a
and b are not independent of one another, why are they separate
reducers?
Well if we talk about correctly structuring the reducer, cachedInput and todoList should live in single reducer. There is no need to create another reducer. Also I guess you need to grab the input value all at once(when user clicks on submit) and send it to store.
If still you want to create seperate reducers then you can follow the approach given below :
Answer to How to access or share data already in store, inside the reducers?
Don't use combineReducers.
Example
replace this code
export const a = combineReducers({
cachInput,
newTodo
});
with
export default (state = {}, action) => {
return {
cachInput: cachInput(state.app, action, state),
newTodo: newTodo(state.posts, action, state),
};
};
reducer would be like
const reducer = (state = initialState, action, root) => {....}
and you can access the previous state from root
onChange event that triggers an action and sends event.target.value to a reducer and stores each character the users types to a property of the store object
Practically, this is not right way to use to redux->action->reducers->store considering you are submitting the form to send the same data to reducers.
This will caused un-necessary rendering of component connected with redux#connect if you have not handled shouldComponentUpdate /componetWillRecieve/getDerivedStateFromProps nicely
Instead store the each character/string that you typed, inside component state and once the user submit it, dispatch the action to pass the string/characters to reducer's.
You are using two different state(or say initialState) for each reducer. Hence update in one object is not reflecting in the other.
You need to use the shared initialState for both of the reducer and your problem is solved.
You can keep initialState in different file and you can import in both the reducers.
Consider below code sample:
1.InitialState.js
export default initialState = {
todoList: [
{ text: "fake todo" },
{ text: "fake todo" },
{ text: "fake todo" }
],
cachedInput: ""
};
2.Now in CachDataOfTodoInput.js
import initialState from "InitialState";
export function cachInput(state = initialState, action)
3.Same in SubmitDataToTodo.js
import initialState from "InitialState";
export function submitNewTodo(state = initialState, action)
Hence intialState is shared between your reducers and you will be able to access data in each reducer.
If you want to load your application with initial state that contains previously cached data and if you are using client side rendering, you may insert cached data in redux store into local storage and use that as initial state.
If you just want data to be imported to the other reducer after form submission, you may use redux-thunk. It lets you load current state in your submit action and pass the catches values into the other reducer and store them.
if you are using ssr and you want data be loaded on server there is no way unless saving data on server ( in a file or database), read the data when request is received on server and update your redux store. It will load data all over your app.

Is there a way to store part of application state in URL query with React+Redux?

I'm using React, Redux and React Router. I want to store most of application state in redux store, and some bits of it in URL query. I believe there's two ways of doing that:
syncing redux store with URL query after each state change
don't store "some bits" in redux store, instead have some way (similar to how reducers work) to change URL query as if it was another store
I'm not sure if first way is even possible, since serialized state length may exceed URL limit. So I probably should go with the second.
For example, I have several checkboxes on a page and I want their state (checked/unchecked) to be mirrored in URL, so I could send my URL link to somebody else and they would have the same checkboxes checked as I do. So I render my checkboxes like that:
export class MyComponent extends React.Component {
handleCheckboxClick(...) {
// Some magic function, that puts my urlState into URL query.
updateUrlState(...)
}
render() {
// 'location' prop is injected by react router.
const urlState = getUrlState(this.props.location.query);
// Let's say 'checkboxes' are stored like so:
// {
// foo: false,
// bar: true,
// }
const { checkboxes } = urlState;
return (
<div>
{ checkboxes.map(
(checkboxName) => <Checkbox checked={ checkboxes[checkboxName] } onClick={ this.handleCheckboxClick }>{ checkboxName }</Checkbox>
)}
</div>
)
}
}
What I want magic updateUrlState function to do is get current URL query, update it (mark some checkbox as checked) and push results back to URL query.
Since this improvised URL state could be nested and complex, it probably should be serialised and stored as JSON, so resulting URL would look somewhat like that: https://example.com/#/page?checkboxes="{"foo":false,"bar":true}".
Is that possible?
Refer to this
Modify the URL without reloading the page
You can simply dispatch an action when the checkbox is checked/unchecked that will invoke your magic url function to change the url using window.history.pushState()
function changeUrl() {
isChecked = state.get('checkBox');
window.history.pushState(state, '', 'isChecked=' + isChecked)
}
The first argument above is for when user clicks the back button, the browser will emit popstate event, and you can use the state to set your ui. See https://developer.mozilla.org/en-US/docs/Web/API/History_API
Then when the other user paste the url, you would want to just parse the url and set the correct state in the store in componentDidMount.
function decodeUrl() {
let params = window.location.href.replace(baseUrl, '');
// parse your params and set state
}
FYI: URL does not support certain characters (e.g. '{'). So you will want to parse them into query params, or encode them. Try this tool http://www.urlencoder.org/
If I am getting your issue right, than redux middleware can really help you here. Middleware is a bit like a reduces, but sits in between the flow from the action to the reducer. Best example for such a use case is logging and I have developed middleware, which handles all remote requests. Basically you get the dispatched action in the middleware, there you can analyze it. In case of an action which should change the query parameter of the applycation, you can dispatch new actions from there.
So your code could look like this:
const queryChanges = store => next => action => {
if (/*here you test the action*/) {
//here can change the url, or
return dispatch(actionToTriggerURLChange());
} else {
return next(action);
}
}
In case you neither call return next(action), or dispatch a new one, just nothing will happen, so keep that in mind.
Have a look at the docs and figure out all the workings of middleware works and I guess this approach will be the most »non-repetitive« in this case.

Categories

Resources