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))
);
Related
I'm building a web app that has role permissions based, admin, user and also products, product A, B, C, etc. I get these data from a backend api.
Currently, I'm using local storage and useContext hook to save and manipulate these data, but an user that knows it, can easily change this information and manipulate the front end, so I'm wondering now which approach I can use here.
My wish (if it's possible) is to get these information by backend after the login, and reuse the data freely in other components, just importing it, like an useContext hook.
I know that there is Redux, but since I'm using next.js, from what I saw, every rendering it will lose/refresh data, so it won't be usefull here.
I'm also using SWR, so, I tried to get these data from cache.get('key'), but the SWR call must be on the same component to get the data properly from the key cached. It's not working if a call the SWR on the e.g home page, and try to get it in other generic component.
What do you people suggest to go here?
Thanks!
I think you should authenticate your user, then store their access key and identifier in localStorage and global state.
When users access an authorization required page.
You'll check for the access token if it doesn't exist on both global state and localStorage. Redirect (or alert) the authorization error.
If there is an access token. Then send a request to the server with that access token. The server will be in charge of authorizing progress. And you will handle the logic on the front end based on the response from the server.
The thing is the authorization (checking permission for instance) should be done in the backend, not on the frontend
I don't know whether you can manipulate the implementation of the backend API or not. Hope it helps
Following the answers, I created a useContext to use in any component that I need.
Here is what I have in my context:
const [userRoles, setUserRoles] = useState<string[] | undefined>([])
const getUsersRoles = useCallback(async () => {
const getUserRoles = await UsersService.getUsersRoles(userInfo.idUsuario)
setUserRoles(getUserRoles.data)
// eslint-disable-next-line react-hooks/exhaustive-deps
}, [])
Note: UsersService.getUsersRoles function is integration with the API
And how I use it in the component I need:
const { userRoles, getUsersRoles } = useAuth()
if (userRoles?.length === 0) {
getUsersRoles()
return <LoadingGIf tip="Carregando opções..." />
}
With this, I have the data I need here userRoles. And IF the user reload/refresh the page, getUsersRoles is requested and set the data to the userRoles
The downside to this, at least for me, is that I have to add this:
const { userRoles, getUsersRoles } = useAuth()
if (userRoles?.length === 0) {
getUsersRoles()
return <LoadingGIf tip="Carregando opções..." />
}
for every component I need to use the roles data, but I believe that's it. It's working fine and isn't request extra any endpoints of API.
If anyone has a contribuitions or improves to do in the code, fell free to do it.
I have to do full offline functionality like all redux store data should remain after page refresh/offline(network connection lost).
When user goes offline previously stored data should be used in viewing pages & for suppose i have big forms(ex: employee form with all work experience and personal details)suddenly if user loose network, data should not be lost.. should be saved somehow & after that when user is online have to store data to DB
I am using react -18.0.0 version with react-redux & redux-saga.What are the possible ways?
lets say users are required to log in to system .once login is successfull save that
data to local storage then dispatch loginsuccess with user and token as payloads
execute a function inside useEffect in app.js to get back saved data from local storage and dispatch
loginsuccess with those data
inside signup function:
let res=await axios.post("/signin",{user});
let{user,token}= res.data;
localStorage.setItem("user", JSON.stringify(user));
localStorage.setItem("token",token);
dispatch({ type: "loginsuccess",payload:{user,token}});
inside app.js :
useEffect(() => {
dispatch(isUserLoggedIn());
}, []);
inside isUserLoggedIn :
const localstorage = localStorage.getItem("user");
if (localstorage) {
const user = JSON.parse(localStorage.getItem("user"));
dispatch({ type: "loginsuccess",payload:{user}});
} else {
dispatch({ type: "loginfailed",payload:{error:"please log in"}});
}
One way can be to store your form(or state) in localstorage. Another way can be use library redux-persist .
I think your question is similar to the one asked here. Have a look here for more ideas.
Another approach to store your data, is to make use of the IndexedDB API, it already has a great support on the latest browser versions caniuse.
There is a really good library that make use of this api but
combined with promises idb.
After picking up how you're gonna do it, you can have a watcher saga that reacts to the online / offline status and do the appropriated synchronization.
Also I think it's important to mention that all of the above will only take care of the data, you still need to make sure to make the JS and CSS to be available offline. on web.dev there is a great induction on this subject.
I am implementing an Instagram clone. In my current app, I have two contexts:
UsersContext
PostsContext
In the users context, I store all the users data, and in the other one, I store the posts data (images, dimensions, description, totalLikes, totalComments, and location).
As you can see, I am not storing nothing about the post owner... neither his id, nor his avatar or username... literally there is no data about users in the PostsContext.
In my card component, I am consuming both contexts, in order to synchronize all the UI with the most up-to-date data which is relative to users and posts.
My question is: should I include the posts owners' data inside the PostsContext? Is there any kind of pattern or something? This is my first time using React Context API, I am a little lost. Is the only purpose of contexts to make things globally and synchronize all routes?
Any example?
Generally I would order the providers this way.
<UserContextProvider>
...
<PostContextProvider uid={uid}>
...
</PostContextProvider>
</UserContextProvider>
Where I make a provider function that will look like
export function PostContextProvider({uid, children}) {
const posts = getPosts(uid);
return (
<PostContext.Provider values={{posts}}>
{children}
</PostContext.Provider>
);
}
then access the posts.
I am working on a react-with-redux application I am working with the library redux-undo and as the capability of the library goes, it listens to a list of actions and reverts to the previous state when the undo happens.
Scenario: I have a page where a list item will be created/deleted and a API call is made whenever these actions happen. The user can undo both the create and delete operations.
I would like to know if there is any way to know the latest action that has been dispatched.
For example: If the user creates a list item and clicks undo, I would like to know that the latest action that was dispatched was create, so that I can revert the create(delete the list item by making an API call).
Like wise, If the user deleted an list item I would like to know that the latest action that was dispatched was delete, so that I can revert the delete(create the list item again by making an API call, fetching the details from past shape of state and sending the details of the deleted list item)
Please let m know if there is any way to achieve this?
You can use store.subscribe, as stated here:
The easiest way is to have a reducer that remembers just the last action:
function lastAction(state = null, action) {
return action;
}
Then you can use store.getState().lastAction, assuming you did
something like
import { combineReducers, createStore } from 'redux';
const rootReducer = combineReducers({
someReducer,
someOtherReducer,
lastAction // <-- use it!
});
const store = createStore(rootReducer);
store.subscribe(() => {
console.log(store.getState().lastAction);
});
You would need to find an alternative storage space to store your latest action that is unaffected by redux-undo but also global so you can access it anywhere you need.
I recommend a local storage solution.
In your reducer, you can add a statement to set which was the latest dispatched action into browser storage:
...
case CREATE: {
localStorage.setItem("latestAction", "CREATE");
return someNewState;
}
case DELETE: {
localStorage.setItem("latestAction", "DELETE");
return someNewState;
}
...
Then when you want to get that value from anywhere in code:
localStorage.getItem("latestAction");
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.