in a react native app, i'm using redux. currently the whole app have single store and i use redux-persist to cache store to localstorage.
my app is username and password protected, you must create account to use it.
now i want to provide ability so that my user can switch between his accounts -if he have more than one account- . this is causing lots of trouble because now i have to clear storage and reset state everytime user switch between accounts.
so i was considering may be i can use multiple stores, one for every users ?
for example my app state looks like
{
chat:{},
highscores:{},
gameHistory:{},
}
now if a user have account lets say User1#gmail.com the state will be populated with his data. and his state will be saved to LocalStorage,
once he switch account to User2#gmail.com now i have to reset the app to its initialState, then somehow load the User2 state from localStorage
i dont want the state of the app to be lost everytime user switch between accounts.
so i was considering may be in this case it would be a good option to use a multiple Redux Stores, one for every user.
did anyone had an app that is designed to be used by multiple users before ?
how can we do this in redux ?
Well Answer above work fine, but since i'm using ImmutableJs, having a deeply nested objects can really be hard to handle.
so i ended up namespacing the Storage Key with user_id.
so now when ever i switch user, i just flush the whole store with this specefic user data from localStorage, or AsyncStorage.
i wrapped rootReducer in a simple reducer to handle this.
function makeRootReducer(rootReducer){
return function reducer(state, action){
if(action.type==='SWITCH_USER'){
//LOAD USER DATA..
const data = JSON.parse(localStorage.getItem("store.user."+action.id)||"{}");
return makeInitialData(data); //this just return initialData.
}
let newState = rootReducer(state, action);
//simple save state to localStorage if state changed
if(state !== newState)localStorage.setItem('store.user.'+state.user_id',JSON.stringify(newState);
return newState;
}
}
I don't think having a store for each user is a good idea. See this SO answer: https://stackoverflow.com/a/33633850/3794660
Why don't you namespace the data you have in your reducer by user id? Something like this:
{
currentUserId: "1",
chat:{ "1": { // Chats for user id 1 }, "2": { // Chats for user id 2 }},
highscores:{ // Same structure as above },
gameHistory:{ // Same structure as above },
}
When you switch user account, you simply update the currentUserId in the state.
I'd recommend using selectors to encapsulate the logic to read the data from the store.
A simple selector to get all the chats for the current account could look like this:
const getCurrUserId = state => state.currentUserId
const getChats = state => {
const userId = getCurrUserId(state);
return state.chat[userId];
}
You then use your simple getChats selector in your mapStateToProps to pass the data to your components. In this way you encapsulate the logic to retrieve the data from the state and your components don't need to know these details, so you're free to change your strategy if you need to.
Related
I have large amount of data that I'm storing in local storage using react persist. What I need is to know when persist data were restored in my reducer (when app is loading). I need to validate data version and I need to generate lookup object (which redux-storage us unable to store, probably because because it has around 65405 records/fields).
Anyway I would like to know when react-persist is loading my data so I can work with them. How do I achieve this?
There are two special actions in redux-persis: PERSIST and REHYDRATE.
The PERSIST action has been dispatched and then with REHYDRATE, the saved object of the store will be load and injected into the current redux store. So, you can import them into your reducer functions and be aware of persisting status.
You need to manipulate the data before re-hydrated them with persistor, but I recommend doing a simpler approach to handle this case.
For example, there is a data value (which is contain 65405 records):
case DATA_ARRIVED_SUCCESSFULLY:
return {
...state,
data: actin.payload
}
Now, before persisting and before putting the payload on data, you can check and validate your data:
const sampleValidatorData = (myData) => {
// do something with myData
}
case DATA_ARRIVED_SUCCESSFULLY:
return {
...state,
data: sampleValidatorData(actin.payload)
}
Now, your saved data (with persistor) will be valid.
Basically I want to implement an architecture of the type
UserManager.getUser(22)
getUser() -> ReduxStore -> (Does not contain an user with ID 22) -> Goes to User Provider -> User Provider goes to API and returns User object.
Redux Store then saves for subsequent requests and returns User object.
Redux has unidirectional data flow, so the writing and the reading of data are decoupled.
Components read Redux data by subscribing to the store via connect or useSelector, and they write data via disptaching actions in the store.
A selector takes in state and returns a subset of the state, but it does not change the state. A dispatched action can change the state, but it does not return any state.
CQRS (Command Query Responsibility Segregation) is one of the motivations behind Redux. The idea of CQRS is basically to:
use a different model to update information than the model you use to read information
In Redux the update-model is the actions and the read-model is the selectors. To combine them into a single "provider" would be to defeat the purpose of Redux's design.
But if you absolutely had to conflate the two concerns, it might be possible to somehow combine a selector and action-dispatch with a thunk. Again, though, it would not be idiomatic Redux.
Yes we call them action creators. Let's say you're using redux thunk for side effects so getUser will be an action creator that'll first query redux store to see if there is data available if not it'll fetch it from server and store in the redux store like this:
function getUser(id) {
// getState is a function that gives us access to entire redux store
return (dispatch, getState) => {
try {
let user = getState().users.find(x => x.id === id) || null;
if (user) {
dispatch({ type: 'GET_USER', payload: user })
return;
}
user = fetchUserFromServer(id);
dispatch({ type: 'GET_USER', payload: user })
} catch(error) {
// handle error here
}
}
}
Now when next time getUser is called there will be data for that user in the redux store and a call to server will be avoided.
Hope it helps :)
In my web app, an authenticated user can pick songs from his spotify playlist to play at a party. I want guests (nonauthenticated users) to be able to view the picked songs and vote on their favorite songs on their own device (probably a phone), which means that they will be able to alter the state of the app.
I am using a Mongo, Express, React with Redux, Node full stack.
My plan is to store the picked songs and put it in my database and my app will generate a dynamically created react route that will GET request the songs from the database and display them on this route. The guests will only have access to this route.
How do I connect my guests to my app's redux store when they only have access to the route I give them? Is it even possible to connect them?
Following the comments below, I hope that I understand now what you ask.
The application needs to always update the redux store to the latest contents of the database - regardless if it is a logged-in user or a guest. Both types of users need access to the store and should fire-up actions if and when they need to update the store.
redux-persist that I mentioned in the comments to the question takes care of making the state persistent in the browser, in case you refresh the page. May be needed as well (try to display a list of songs and then refresh the page), but not relevant to your question.
You can create a middleware and add it to the store
import { createStore, applyMiddleware, compose } from "redux";
const customMiddleWare = store => next => action => {
if(store.user.isAuthenticated === true){
next(action)
}else{
// You are only allowing non-authentic user to see the list. only those actions will be accessible to that user
if(action.type === 'FETCH_SONGS'){
next(action)
}
}
};
const middleware = [customMiddleWare];
const store = createStore(
greetingReducer,
window.__REDUX_DEVTOOLS_EXTENSION__ && window.__REDUX_DEVTOOLS_EXTENSION__(),
compose(applyMiddleware(...middleware))
);
For example I have two components - ListOfGroupsPage and GroupPage.
In ListOfGroupsPage I load list of groups from the server and store it to the state.groups
In route I have mapping like ‘group/:id’ for GroupPage
When this address is loaded, the app shows GroupPage, and here I get the data for group from state.groups (try to find group in state via id).
All works fine.
But if I reload page, I'm still on page /group/2, so GroupPage is shown. But state is empty, so the app can't find the group.
What is the proper way to load data in React + Redux? I can see this ways:
1) Load all data in root component. It will be very big overhead from traffic side
2) Don't rely on store, try to load required data on each component. It's more safe way. But I don't think that load the same data for each component - it's cool idea. Then we don't need the state - because each component will fetch the data from server
3) ??? Probably add some kind of checking in each component - first try to find required data in store. If can't - load from the server. But it requires much of logic in each component.
So, is there the best solution to fetch data from server in case of usage Redux + ReactJS?
One approach to this is to use redux-thunk to check if the data exist in the redux store and if not, send a server request to load the missing info.
Your GroupPage component will look something like
class GroupPage extends Component {
componentWillMount() {
const groupId = this.props.params.groupId
this.props.loadGroupPage(groupId);
}
...
}
And in your action...
const loadGroupPage = (groupId) => (dispatch, getState) => {
// check if data is in redux store
// assuming your state.groups is object with ids as property
const {
groups: {
[groupId]: groupPageData = false
}
} = getState();
if (!groupPageData) {
//fetch data from the server
dispatch(...)
}
}
I recommend caching the information on the client using localstorage. Persist your Redux state, or important parts of it, to localstorage on state change, and check for existing records in localstorage on load. Since the data would be on the client, it would be simple and quick to retrieve.
The way I approach this is to fetch from the server straight after the store has been created. I do this by dispatching actions. I also use thunks to set isFetching = true upon a *_REQUEST and set that back to false after a *_SUCCESS or *_FAILURE. This allows me to display the user things like a progress bar or spinner. I think you're probably overestimating the 'traffic' issue because it will be executed asynchronosly as long as you structure your components in a way that won't break if that particular part of the store is empty.
The issue you're seeing of "can't get groups of undefined" (you mentioned in a comment) is probably because you've got an object and are doing .groups on it. That object is most likely empty because it hasn't been populated. There are couple of things to consider here:
Using ternary operators in your components to check that someObject.groups isn't null; or
Detailing in the initialState for someObject.groups to be an empty array. That way if you were to do .map it would not error.
Use selectors to retrieve the list of groups and if someObject.groups is null return an empty array.
You can see an example of how I did this in a small test app. Have a look at specifically:
/src/index.js for the initial dispatch
/src/redux/modules/characters.js for the use of thunks
/src/redux/selectors/characters.js for the population of the comics, series, etc. which are used in the CharacterDetails component
I have created an application where user can search information related to movies using react and redux. While searching user can apply some filter(For eg. time duration). I want this filter to be active till user unselect them even after user reloads the page.
Problem:
Current scenario user apply filter application will dispatch an event and store the filter information in Redux state.
But as soon as user refresh the page information about the filter get lost.
Solution Tried:
I have tried one solution using session storage and local storage, but I am not convinced with the solution.
It would be great if somebody can show better way of solving this problem if available.
For some simple states, like current value of filter, it would be better to use location.
For example, you have the following page: http://example.com/users.
Then you can preserve filter like this: http://example.com/users?group=admin.
The benefit of this approach is simple: you explicitly say to user the actual state of the page, he can copy that, save bookmark, or send to somebody else.
To achieve this in React code, you can do the following (I assume that you have React-router in your app):
class UsersPage extends React.Component {
// should be called somewhere in onClick
filterUserGroup(groupName) {
this.props.router.push({
pathname: this.props.location.pathname,
query: {
group: groupName
}
});
}
componentWillReceiveProps(nextProps) {
if(nextProps.location !== this.props.location) {
//filter was changed, you can apply new filter value
this.setState({
selectedGroup: nextProps.location.query.group
});
}
}
}