React Application and User Roles - javascript

Is there a "built in" way to scope with different user roles in React Application? I want certain tabs, menus and links (routes) to be available only for certain users and not for others. Also, the content and options of many views will vary depending on the role. I know how to manage roles in the back end and retrieve them during the authentication in the JWT token, but what is the best way to parametrize the client-side state and representation based on the roles? Is the Redux and render logic conditions way to go, or is there a more streamlined solution, which necessarily doesn't demand the client browser to know all possible states for different roles prior to authentication?

I can suggest write a higher order component which takes roles who can see the feature.
const AuthorizeOnly = ({ children, allowedRoles, user }) => (
allowedRoles.includes(user.role)
&& (
<React.Fragment>
{children}
</React.Fragment>
)
);
// some component
loggedInUser = {
name:'john',
role:'admin'
}
render(){
<div>
<AuthorizedOnly allowedRoles={['admin']} user={loggedInUser}>
<p>only admin can see this.</p>
</AuthorizedOnly>
</div>
}
You can tweak the logic inside AuthorizedOnly component based on your business logic.
For Router you can use the similar component which returns a Route based on your condition

Related

Gatsby fetch data from local storage best practice

I would like to fetch data from local storge (in runtime) every time the app starts, then I store it in a store.
Gatsby docs explain the way to do this:
https://www.gatsbyjs.com/docs/conceptual/data-fetching/
Basically just use the useEffect hook in a page and get the data from local storage. However, I would like to get this data independently of the page being visited. For example, if I fetch the data on the index page, and the user refreshes another page, the data won't be fetched. I would like to do it in the equivalent of App.tsx file in a regular React app.
My current solution is to do it in wrap-pages file:
const MyLocalStorage = ({ children }) => {
const { getLocalStorage} = fromStore()
useEffect(() => {
getLocalStorage() // fetches data from local storage
}, [])
return null
}
export function wrapPagesDeep({ element }) {
return (
<>
<MyLocalStorage/>
{element}
</>
)
}
This however doesn't make much sense. This file is intended for wrapping components, not for data fetching. What would be the correct way to do that please?
There are multiple ways depending on your architecture, design system, and use cases (for example, from a provider to a wrapper, from an isolated service to a controller, etc.).
There is no such thing as "best practice" without knowing everything involved in the decision: making some super complicated and isolated logic (like adding an MVVM: controller, stores, etc.) may look good but can be an extremely bad practice for a simple scenario, and vice-versa: an easy and straightforward approach can be a bad solution for a complex app.
Following your approach, I think it could be easily isolated (and reused) by moving this logic into a Layout (or a wrapper that wraps your application) and adding a location prop to it. Something like:
const Layout = ({ children, location = {} }) => {
const { getLocalStorage} = fromStore()
useEffect(() => {
getLocalStorage() // fetches data from local storage
}, [location])
return (
<>
<main>{children}</main>
</>
)
}
export default Layout
Then, in every use of Layout:
const SomePage = ({ location }) => {
return (
<Layout location={location}>
<h1>Some content</h1>
</Layout>
);
};
Note: location prop is inherited by default in all top-level components (pages and templates) as you can see in the docs
So every time the location changes, you will fetch the local storage data. This can be easily moved to a provider that updates the value automatically. You will only need to wrap your application accordingly.

Create multiple resources from the same endpoint in React Admin to have different filters applied

I need to create different routes in React admin, all based on the same endpoint but with a different filter. My need is that I need a Menu entry to show all properties with status = approved, all properties with status = review and so on.
I tried doing this:
export default function App() {
return (
<Admin
loginPage={CustomLoginPage}
dataProvider={dataProvider}
authProvider={authProvider}
>
<Resource
name="properties"
options={{ label: 'Properties in review' }}
icon={UserIcon}
list={PropertyReviewList}
show={PropertyShow}
edit={PropertyEdit}
/>
<Resource
name="properties"
options={{ label: 'Properties Approved' }}
icon={UserIcon}
list={PropertyApprovedList}
show={PropertyShow}
edit={PropertyEdit}
/>
</Admin>
);
}
but this is not working as only the last defined property is showing. What is the best way to achieve what I am trying to achieve?
If I understood you correctly, you need a component that renders different data depending on some part of the link that can change.
This can be achieved using query params. Here is an interactive code example from react router docs that does what you need:
https://v5.reactrouter.com/web/example/query-parameters
So, you will have a link for your page like "/properties?status=review" where status is the query param. You check it in your component to show data depending on its value
status = approved, all properties with status = review
Enter these properties in query. Example:
axios.get("/api/...?status=approved")
axios.get("/api/...?status=review")
Makes these api calls where you need it. You can have a single controller in backend to handle both the requests.

When doing role based routing in react, is use of switch statement and showing different component based on role a security issue?

I have the following code, its simple and does the job. But is there security issues with this? This is my first time doing role based authorization so trying to understand what I can do better.
<Route path={"/"} component={RootPage}></Route>
export default const RootPage({role}) => {
switch(role) {
case USER: return <MainPageUser />
case ADMIN: return <AdminPage />
default: return <MainPage />
}
}
There are a few options you can choose from, all of them are (almost) just as (un)safe as the other. It's javascript, if someone wants to, then can change it as it's client side.
You can do the switch as you are doing, or something like:
{role==="admin" && <AdminStuff />} // the component only gets rendered if the first part is true
{role==="user" && <UserStuff />} // so in this case it's one or the other.
You can also create a component thats called a voter:
function RoleVoter({grantedRole, requiredRole, children){
render(){
return grantedRole===requiredRole ? children : null;
}
}
// Example:
<RoleVoter grantedRole={role} requiredRole={ADMIN}> <AdminStuff/> </RoleVoter>
Each varies in complexity and usability, they all have their benefits/drawback. The switch is usefull if "only one case may match". The && method is good for quick coding, but will result in hardcoding very fast, hard to maintain. The RoleVoter aproach is more complex and might be overkill, but you can now throw every role in there you want. And expand it to understand multiple roles (eg ADMIN and VIEW_ORDERS if you want that kind of level of security).
All of these are about as safe as the other. It's Javascript, at happens clientside, nothing you can do about that. What is more important, is that you pick a solution which is easy to implement/understand by the developers. The reason for that is that the easier security becomes to implement, the more often you will implement it.
Then where lies the real security? Easy: Serverside. Imagine you hack some javascript and you get it to show you an admin page, but the server never gave it the actual content. Pretty useless. You also create the security checks serverside based on the current user.
A somewhat standard method is that you make voters (small functions) that each test two things:
Can the user add/view/edit/delete this type of item (in general)? E.g. does the user have EDIT_USER permission when trying to edit a user? Or a VIEW_ORDER when trying to view an order?
Can the user add/view/edit/delete this specific item? E.g. if you can only change an order when it's your own, is it actually yours?

Is connect() in leaf-like components a sign of anti-pattern in react+redux?

Currently working on a react + redux project.
I'm also using normalizr to handle the data structure and reselect to gather the right data for the app components.
All seems to be working well.
I find myself in a situation where a leaf-like component needs data from the store, and thus I need to connect() the component to implement it.
As a simplified example, imagine the app is a book editing system with multiple users gathering feedback.
Book
Chapters
Chapter
Comments
Comments
Comments
At different levels of the app, users may contribute to the content and/or provide comments.
Consider I'm rendering a Chapter, it has content (and an author), and comments (each with their own content and author).
Currently I would connect() and reselect the chapter content based on the ID.
Because the database is normalised with normalizr, I'm really only getting the basic content fields of the chapter, and the user ID of the author.
To render the comments, I would use a connected component that can reselect the comments linked to the chapter, then render each comment component individually.
Again, because the database is normalised with normalizr, I really only get the basic content and the user ID of the comment author.
Now, to render something as simple as an author badge, I need to use another connected component to fetch the user details from the user ID I have (both when rendering the chapter author and for each individual comment author).
The component would be something simple like this:
#connect(
createSelector(
(state) => state.entities.get('users'),
(state,props) => props.id,
(users,id) => ( { user:users.get(id)})
)
)
class User extends Component {
render() {
const { user } = this.props
if (!user)
return null
return <div className='user'>
<Avatar name={`${user.first_name} ${user.last_name}`} size={24} round={true} />
</div>
}
}
User.propTypes = {
id : PropTypes.string.isRequired
}
export default User
And it seemingly works fine.
I've tried to do the opposite and de-normalise the data back at a higher level so that for example chapter data would embed the user data directly, rather than just the user ID, and pass it on directly to User – but that only seemed to just make really complicated selectors, and because my data is immutable, it just re-creates objects every time.
So, my question is, is having leaf-like component (like User above) connect() to the store to render a sign of anti-pattern?
Am I doing the right thing, or looking at this the wrong way?
I think your intuition is correct. Nothing wrong with connecting components at any level (including leaf nodes), as long as the API makes sense -- that is, given some props you can reason about the output of the component.
The notion of smart vs dumb components is a bit outdated. Rather, it is better to think about connected vs unconnected components. When considering whether you create a connected vs unconnected components, there are a few things to consider.
Module boundaries
If you divided your app into smaller modules, it is usually better to constrain their interactions to a small API surface. For example, say that users and comments are in separate modules, then I would say it makes more sense for <Comment> component to use a connected <User id={comment.userId}/> component rather than having it grab the user data out itself.
Single Responsibility Principle
A connected component that has too much responsibility is a code smell. For example, the <Comment> component's responsibility can be to grab comment data, and render it, and handle user interaction (with the comment) in the form of action dispatches. If it needs to handle grabbing user data, and handling interactions with user module, then it is doing too much. It is better to delegate related responsibilities to another connected component.
This is also known as the "fat-controller" problem.
Performance
By having a big connected component at the top that passes data down, it actually negatively impacts performance. This is because each state change will update the top-level reference, then each component will get re-rendered, and React will need to perform reconciliation for all the components.
Redux optimizes connected components by assuming they are pure (i.e. if prop references are the same, then skip re-render). If you connect the leaf nodes, then a change in state will only re-render affected leaf nodes -- skipping a lot of reconciliation. This can be seen in action here: https://github.com/mweststrate/redux-todomvc/blob/master/components/TodoItem.js
Reuse and testability
The last thing I want to mention is reuse and testing. A connected component is not reusable if you need to 1) connect it to another part of the state atom, 2) pass in the data directly (e.g. I already have user data, so I just want a pure render). In the same token, connected components are harder to test because you need to setup their environment first before you can render them (e.g. create store, pass store to <Provider>, etc.).
This problem can be mitigated by exporting both connected and unconnected components in places where they make sense.
export const Comment = ({ comment }) => (
<p>
<User id={comment.userId}/>
{ comment.text }
</p>
)
export default connect((state, props) => ({
comment: state.comments[props.id]
}))(Comment)
// later on...
import Comment, { Comment as Unconnected } from './comment'
I agree with #Kevin He's answer that it's not really an anti-pattern, but there are usually better approaches that make your data flow easier to trace.
To accomplish what you're going for without connecting your leaf-like components, you can adjust your selectors to fetch more complete sets of data. For instance, for your <Chapter/> container component, you could use the following:
export const createChapterDataSelector = () => {
const chapterCommentsSelector = createSelector(
(state) => state.entities.get('comments'),
(state, props) => props.id,
(comments, chapterId) => comments.filter((comment) => comment.get('chapterID') === chapterId)
)
return createSelector(
(state, props) => state.entities.getIn(['chapters', props.id]),
(state) => state.entities.get('users'),
chapterCommentsSelector,
(chapter, users, chapterComments) => I.Map({
title: chapter.get('title'),
content: chapter.get('content')
author: users.get(chapter.get('author')),
comments: chapterComments.map((comment) => I.Map({
content: comment.get('content')
author: users.get(comment.get('author'))
}))
})
)
}
This example uses a function that returns a selector specifically for a given Chapter ID so that each <Chapter /> component gets its own memoized selector, in case you have more than one. (Multiple different <Chapter /> components sharing the same selector would wreck the memoization). I've also split chapterCommentsSelector into a separate reselect selector so that it will be memoized, because it transforms (filters, in this case) the data from the state.
In your <Chapter /> component, you can call createChapterDataSelector(), which will give you a selector that provides an Immutable Map containing all of the data you'll need for that <Chapter /> and all of its descendants. Then you can simply pass the props down normally.
Two major benefits of passing props the normal React way are traceable data flow and component reusability. A <Comment /> component that gets passed 'content', 'authorName', and 'authorAvatar' props to render is easy to understand and use. You can use that anywhere in your app that you want to display a comment. Imagine that your app shows a preview of a comment as it's being written. With a "dumb" component, this is trivial. But if your component requires a matching entity in your Redux store, that's a problem because that comment may not exist in the store yet if it's still being written.
However, there may come a time when it makes more sense to connect() components farther down the line. One strong case for this would be if you find that you're passing a ton of props through middle-man components that don't need them, just to get them to their final destination.
From the Redux docs:
Try to keep your presentation components separate. Create container
components by connecting them when it’s convenient. Whenever you feel
like you’re duplicating code in parent components to provide data for
same kinds of children, time to extract a container. Generally as soon
as you feel a parent knows too much about “personal” data or actions
of its children, time to extract a container. In general, try to find
a balance between understandable data flow and areas of responsibility
with your components.
The recommended approach seems to be to start with fewer connected container components, and then only extract more containers when you need to.
Redux suggests that you only connect your upper-level containers to the store. You can pass every props you want for leaves from containers. In this way, it is more easier to trace the data flow.
This is just a personal preference thing, there is nothing wrong to connect leaf-like component to the store, it just adds some complexity to your data flow, thus increase the difficulty to debug.
If you find out that in your app, it is much easier to connect a leaf-like component to the store, then I suggest do it. But it shouldn't happen very often.

Sharing global/singleton data in react app

I'm rewriting a small app to try and better understand React. I'm trying to determine the "correct"/most efficient method of sharing "singleton" data - for example, a user who's been properly authenticated upon login.
Right now the parent "application" component has a user property in its state, which I pass to child components as a prop:
<Toolbar user={this.state.user} />
<RouteHandler user={this.state.user}/>
(I'm using react-router). This works, and in read-only cases like this, isn't terrible. However, my actual login form component (which is a route, and would be inside RouteHandler), needs some way to "set" the new user data, so I also need to pass in some callback:
<RouteHandler onAuthenticated={this.setUser} user={this.state.user}/>
Not a big problem, except for the fact that now this method is available to every "route" handled by RouteHandler.
I've been reading up and it seems like the only alternative is an EventEmitter or Dispatch-style system.
Is there a better way I'm missing? Is an event emitter/dispatcher system worth using when there's really only one or two uses in an app this small?
React Context provides a way to pass data through the component tree without having to pass props down manually at every level. With context, every component nested under a Provider has access to the data, but you need to explicitly read the value.
I recommend using React Hooks with useContext. One way to do this would be to set the value of the context to be an object with setter and getter functions.
import React, { useState, useContext } from "react"
export const UserContext = React.createContext({}); //Initialise
//Wrapper with getter and setter
const App = () => {
const [user, setUser] = useState();
const value = {user, setUser}
return (
<div>
<UserContext.Provider value={value}>
<RouteHandler/>
<AnotherComponent/>
</UserContext>
<ComponentWithoutAccessToUserContext/>
</div>
)
}
const RouteHandler = (props)=> {
const { user, setUser } = useContext(UserContext)
// This component now has access to read 'user' and modify it with 'setUser'
}
const AnotherComponent = () => {
return (<div>Component which could be get access to the UserContext</div>)
}
For singleton - you can just create separate module for user service and import it into module where you define components that need it it.
Other quite similar, but more powerful option, is to use DI container - define your react components as a services in DI container, with dependencies to other services like one for user data. This would be more suitable for universal(isomorphic) app - because, you will be able to easily replace dependencies with specific implementations, or for case when you need to create separate instances for separate scopes(like for user sessions server-side).
Also if using this approach, I would recommend to separate pure react components from logic - you can create separate pure component that receives all data, and callbacks as a props, and than create HoC component in DI container that will wrap it and will pass needed data and callbacks.
If you need DI container - there is a plenty of them, but I will recommend to look at angular 2 di container, or if you would like something simpler - below I referenced my project, it has very simple but yet powerful DI inspired by angular 2 DI(it is easy to pull from that project - just one file + test)).
About notifying components about changes, and organising async logic - you still will need something like EventEmitter to notify components about changes, and you will need to write life cycle callbacks for components to subscribe/unsubscribe from updates… You can do this by hand or creating mixin or HoC to shorten that.
But from my perspective, there is better approach - try reactive programming, and RxJS in particular. It plays very well with react.
If you are interested about options connecting Rx with React - take a look at gist https://gist.github.com/zxbodya/20c63681d45a049df3fc, also it can be helpful about implementing HoC component with subscription to EventEmitter mentioned above.
I have a project that is intended for creating isomorphic(rendered server side, and than same html reused client side) widgets with react.
It has DI container to pass dependencies, and it uses RxJS to manage async logic:
https://github.com/zxbodya/reactive-widgets
One way is to subscribe to an Observable emitted from your data model.
Router.run(routes, Handler =>
Model.subject.subscribe(appState =>
React.render(
<Handler {...appState}/>,
document.getElementById('app')
)
)
);
...appState being the data coming from observable (in this case model), making these your props so you can then feed them to the app like below
<RouteHandler {...this.props} />
and any child component can pick them up with this.props
the answer is more complex that this but if you look at RxJS+React you will get a full working examples of simple data flows

Categories

Resources