Difficulty in Authorization part in Reactjs - reactjs

Having Difficulty in Authorization part in Reactjs.In my app.js got permissions from backend and passed permissions to all components via context api . my main problem arise when user go to usercrudauthorisation.js ,and toogle the switch on / off then Permission value changes accordingly But my context api provides value i.e "u_create": 1,"u_delete": 1 at loading app.js component.The changed toogle switch button value will load when i reload the whole comoponent and re-render the app. I have supplies modulepermissions value according to module_name and what user can perform CRUD in that module via context api. i think route changes doesnot re render the app.js while route changes so that it provides module permissions accordingly? how to solve it? what will be best way to achieve it?
**app.js**
const getPermissions=()=>{}
above function return permission={"id": 1,"module_name": "Product","role": "users","u_create": 1,"u_delete": 1,}
that i have Passed to all components via context api shown in below code
let curr_module=routes.filter(route=>{
if(window.location.pathname===route.path){
return true;
}
return false
})
let curr_module_permission={}
if(curr_module&&curr_module.length>0 ){
let curr_permissions=permissions&&permissions.filter((p)=>{
if(p["module_name"].toUpperCase()=== "PRODUCT" ) {
return true
}
return false
})
if(curr_permissions.length>0){
curr_module_permission=curr_permissions[0]
<UserContext.Provider value={{ permissions : curr_module_permission}}>
**Usercrudauthorization.js**
Two toggle switch button i.e create ,delete i.e ON/OFF

Related

React / Context API / TypeScript : How to init web app and avoid UI flickering at statup?

When a user navigate to my site, I need to initialize the React web app as follow:
If the incoming user has never requested the web app, I want to set the UI language to browser default (navigator.language).
If the incoming user has already visited the site and chosen a prefered language (lang stored in the localStorage), I want to init the UI with this language.
If the incoming user has an account and is already connected (token available in localStorage), I want to auto-connect him and render the app accordingly : login button transformed into a welcome message, UI language set to user preference.
To do so, I'm using React Context API and a defaultUser object.
defaultUser: init a default user
const defaultUser = {
language: 'en_EN',
isConnected: false
}
Context: create a default context
export const AppContext = createContext({
connectedUser: defaultUser,
})
Provider: create the provider with default context
export function AppProvider({ children }: any) {
[...]
const provider = {
connectedUser
}
return (
<AppContext.Provider value={provider}>
{children}
</AppContext.Provider>
)
}
App: init the provider during app start up
export class App extends Component {
static contextType = AppContext
render() {
return (
<AppProvider>
<AppContainer />
</AppProvider>
)
}
}
AppContainer: render the app
export class AppContainer extends Component {
static contextType = AppContext
componentDidMount() {
/** If user is not connected, verify if a stored session exists and use it to connect user */
if (!this.context.connectedUser.isConnected) {
[...do things...]
}
}
The whole mecanism works well except an annoying thing : the web app is systematically initialized with default user values, until the AppContainer::componentDidMount() do the real init job.
This is causing a sort of flickering effect.
I'm struggeling for 2 days on how to fix that, trying to perform Context init before <AppContainer /> rendering, and I'm stuck.
Any recommandations?
EDIT :
For clarity, I'm adding a diagram. Currently :
React App is rendered at start.
Context is initialized at start with default value.
Context is updated when end is reached.
React App is rendered again when end.
Any layout change during these two steps (UI language, UI modification based on user permissions) are clearly visible to the user and generate a sort of flickering.
I found sort of a solution by simply conditionning <AppContainer/> loading, postponing it to the end of the sequence. However instead of having flickering I have now a lag and other unwanted side effects.
The goal would be to differ all the sequence before React Act is rendered, and after Window is available. Then dynamically create the Context, then render all.
I think the point would be resolved if I could dynamically create the AppContext and pass a variable to createContext() during App constructor() or maybe componentWillMount() (not sure when Window is available), but then TypeScript get into play with types issues and I'm still stuck.
You didn't share the code that initializes the context, but I suspect you put the default value to be either a hardcoded value, or navigator.language and therefore experience the flickering. I'd like to suggest two ways to solve this:
Solution 1
Perhaps instead of having a hardcoded default context you could generate the default context programmatically by accessing localStorage.get('lang') or similar? There is a slight drawback to this solution though: You will be mixing concerns of react and the browser, but I think in this case it's an alternative to consider, because it's very simple and obvious to the reader.
Solution 2
Alternatively, when calling ReactDOM.render you could pass down whatever you need from localStorage as a prop to your application and so you keep the browser related logic separate from the pure React stuff.
I hope this makes sense.
Here's my follow-up after Amit suggestions, in case it can help anyone else.
Init Context with functions
Instead of initializing defaultUser with hard-coded values and update it later, I set directly it with a function returning navigator.lang as suggested. This solved the flickering issue on UI labels.
Init data before RectDOM.render
However I still had flickering on UI components for which I have to get the appropriate state from an API call.
Eg, if the incoming user has a valid session token stored in localStorage, the Login button must be disabled. Before doing so, I need to make sure the session token is valid by an async call to the API. I didn't find a way to have it «awaited» by the Context init which seems to be synchronous.
That's where Amit second suggestion get into play. Instead of struggling finding a solution inside React, I did necessary processing before ReactDOM.render, then passing stuffs as props to <Apps/>.
This works pretty well to get and pass the data...
Except that Context API didn't setSate anymore as soon as any of its data was refering to an object from outside the Context. In other word using function calls is ok to init (probably by val), but reference to external objects breaks setState.
Conclusion
As my project is still in early stage, this gave me the chance to get rid of Context API, do the proper init as required, and code the props/states progagation with basic React.

Automatically try to sign in on initial page load using React, ideas on how to do that?

trying to use Microsoft's SSO with React, and I want to
on first load to activate the method to attempt to sign in. So that I don't need to ask the user to click a button, it should just be automatic.
I'm new to react and lifecycle methods, but it doesn't make sense to use componentWillLoad or componentDidLoad because it would just be checking everytime. Basically the whole site should be only viewable if logged in, so I believe setting up my protected paths after this should be doable
Any ideas on how to attempt this?
Edit:
useEffect = () => {
!this.props.account ? this.props.onSignIn() : <AuthWrapper/>
}, [];
You can use the useEffect hook in react and pass in an empty dependency so that the effect is only called once on initial page load. The same thing could be done with componentDidMount. You could put a function within the hook or lifecycle method to check if the user is logged in then show different pages based on if that value. Something like this:
useEffect(() => {
// check here if user is logged in
loggedIn ? showPageA : showPageB
}, []);

Handling loading state in a React app that uses Redux with redux-thunk, going back to component after data has been fetched once

I have a React app that uses Redux with redux-thunk. Given a certain component that needs to fetch information from an API, I call a bound action creator to make that request after it mounts:
componentDidMount() {
this.props.fetchSomething();
}
For the UI to know whether it is in a loading state, I use a loading variable from the application state. My reducer handles three action types for LOADING, SUCCESS, and FAILURE. The loading variable is set to true when the LOADING action is emitted; then it is set to false on SUCCESS and FAILURE.
Thus there is the following in the render method:
render() {
if (this.props.loading) {
return <Spinner />;
}
return (
<div>
This part uses the fetched data, available via this.props.something
</div>
);
}
Because initially this.props.something is null, I also have to check for its existence before rendering the desired template, so the above if-statement becomes:
if (!this.props.something || this.props.loading) {
return <Spinner />;
}
This approach has served me well so far, but there are some issues, one of them being:
The first time I access the page, this.props.something is not yet set. But the second time I access the page, with the app already loaded, that data had already been fetched once, so this.props.something will have been defined. Because of that, there is brief moment in which the if statement condition is FALSE and the actual component template gets rendered.
(1) How would you guys take care of that issue?
(2) And with your approach, how would you handle a component that had to make multiple requests, like five of them, using the same approach above, with a different loading/something variable for each? I can duplicate the same approach above, but get the issue of brief if-statement failure for each resource that is already defined, but not loading.
1) You have two options. U can dispatch some RESET action on componentWillUnmount and reset your store, this.props.something will be null again. Second option is to show data if u already have them, show spinner and when second fetch is success merge it together. (depends on purpose and UI)
2) I have never needed it but what about to store it in redux as map {componentName: loaded} and check if all components are loaded?
Edit: When u set initial value of loading to true you don't need to check something prop.

React : How to safely display a component according to user role

I am trying to display an admin Dashboard in react and Meteor only if the current user is an admin.
I am calling a server method that checks the user permission and rendering the admin component only if this method returns true.
This server call is asynchronous and thus the component is not rendering, usually I manage this asynchronous call with state, but I don't want to expose anything in state here (wouldn't like someone to change the state and access the admin dashboard).
Here is the code:
export default class AdminChecker extends Component {
isItAdmin() {
// Get the id of current user
const userId = Meteor.userId();
if (userId) {
// call a server method which returns true if current user is Admin
Meteor.call('checkAdminId', userId, (err, authorized) => {
if (err) {
console.log(err);
return null;
}
return (authorized) ? <AdminDashboard /> : null;
});
}
}
render() {
return (
<div className="admin-temp-container">
{this.isItAdmin()}
</div>
);
}
}
I think I can have this logic in a parent component and send the result of isItAdmin to adminDashboard as a prop (the adminDashboard component would display information only if its props is true).
But I am insure if this is safe. Could one change the props with Chrome react developer tools or something like that?
Thanks a lot
I think there are two parts to this (and the comments are hinting at both):
First, you should not expect to be able to enforce security on the client. You must implement access control logic on the server (i.e., any API that performs an admin action must check that the user performing the action is an admin).
Once you've done that (and perhaps you already have), then you likely need to use props or state to store whether or not the user is an admin (just like you would store any other data in your app).
The key point is that once you enforce security on the server, then it doesn't really matter if a user manipulates the state to get to the admin dashboard: the server will not let the user view any real data or take any actions anyway.
This is completely unrelated to server-side rendering. You can most certainly build a secure admin dashboard without server-side rendering, as long as the APIs used to fetch the admin data and perform admin actions are implementing their own access control checks.

Getting an error on using setRouteLeaveHook (withRouter)

Currently I'm building a Web App using ReactJS. The app has a registration form.
Now consider, user has started with the registration process. But before submitting the form user leaves this registration page. At this point, say form contains unsaved data and I would like to display a confirmation message saying that Save Changes you have made before leaving this screen.
Below is my code to achieve this
componentDidMount () {
this.props.router.setRouteLeaveHook('/enterprise/enterprise-area/enterprise-details', this.routerWillLeave);
}
routerWillLeave(nextLocation) {
// return false to prevent a transition w/o prompting the user,
// or return a string to allow the user to decide:
if (true) {
return 'Your work is not saved! Are you sure you want to leave?';
}
}
export default withRouter(connect(
mapStateToProps,{
initializeVendorDetails
})(VendorRegistration));
I get the error shown below:
Uncaught TypeError: Cannot assign to read only property '__id__' of /enterprise/enterprise-area/enterprise-details
I went through official documentation and github issues but found nothing. Thanks in anticipation.
#NobuhitoKurose Thanks for your reply. Finally I manage to figure out the problem here.
Yes, My component was not directly connected to route.
I went through withRouter doc where I found that I actually need to provide a route object(this.props.route) as a first parameter instead of route as a string(as I mentioned in above code).
Since my component is not directly connected to route I was getting this.props.route as undefined.
I checked parent component (which is connected to route) and this component has its route prop. So I just pass this route prop from parent component to this current component (where I'm using withRouter) and everything has worked well.
Below is my an updated code
In parent component (which is connected to route)
<VendorRegistration route={this.props.route}/>
Component where I'm using withRouter
componentDidMount() {
this.props.router.setRouteLeaveHook(this.props.route, this.routerWillLeave);
}
routerWillLeave(nextLocation) {
// return false to prevent a transition w/o prompting the user,
// or return a string to allow the user to decide:
// FIXME: update condition as per requirement
if (true) {
return 'You have unsaved information, are you sure you want to leave this page?';
}
}
export default withRouter(connect(
mapStateToProps,{
initializeVendorDetails
})(VendorRegistration));

Resources