Better way to update react component on event - reactjs

I'm making a web-app in which I use React, together with Firebase Authentication. I have a login page and I want it to redirect to a different page when a user is already signed in.
To check if the user is logged in I use a method to retrieve the User object, it returns null when a user is not signed in and a user object with all sorts of data if they are.
I have an if statement in my render method (of the login page) which checks if the user is logged in, but I ran into an issue. When a user first loads up the page it takes around half a second for the Firebase API to retrieve the user object and until that is completed my user object will return null.
This makes for an issue where it seems as though my user isn't logged in even if they are. That causes them not to redirect and stay on the login page until the state is updated in some way after the user object is initialized.
Firebase offers a way to fix this by giving us an onAuthStateChanged() method which allows me to execute a function when a user signs in or logs out.
What I'm doing now is using this method in the constructor method of my Login page class to manually re-render the component, thus redirecting the user when Firebase logs them in. It looks something like this:
export default class Login extends React.Component<Props, State> {
constructor(props:Props) {
super(props)
this.props.firebase.auth().onAuthStateChanged((user) => {
const oldState = this.state
this.setState(oldState)
})
}
render () {
if (this.props.firebase.auth().currentUser) {
return (
<Redirect to="/earn" />
)
} else {
return (
// Login page
)
}
}
}
(I omitted some irrelevant code)
Now this works fine and all but I'm not sure if this is the correct way to do it and I feel like I could make it look a lot nicer, I just don't know how.
Any suggestions or official ways to achieve what I'm doing right now are very much appreciated.

Related

How to prevent to visit login page if user is logged in

How can I prevent to visit login page if user is logged in, I don't want to show user login page if user if user is not logged out. but I have tried with few step but it is not working for me.
I am storing static value in localstorage and if user try to come back into page login page then there I have created a function that user can visit to login page or not
login.js
componentWillMount(){
var id = localstorage.getitem('id')
if(id == "1"){
return <Redirect to="/dashboard"/>
}
}
Here I am able to get it and it going inside if condition as well but not redirecting to dashboard
I don't know what I am doing wrong here.
Your help would be highly appreciated
Thanks in Advance
Redirect works only when it is rendered, which means it is inside render or one of the functions called by it.
Returning a Redirect inside componentDidMount does not redirect the user. For use inside componentDidMount, you can use the imperative API:
this.props.history.push("/dashboard");
Docs for history
Note that this works only if the component is directly rendered inside a Route, otherwise props history will not be present. In other case, you can use withRouter higher order component.

Redirecting after successful action

I am currently working on a authentication project within React Native using the react-navigation package. So far so good, I have 3 Navigators setup, one for loading, one for auth and one for the application.
I have also got the navigation setup with redux so I can access it from any component via a prop, which is exactly what I wanted to do.
However, I have a question. I have done some research and can't seem to figure out the best way to do this.
I am dispatching an action from a press of a button attemptLogin() the attempt login then does what it says on the tin, it attempt to login.
After a successful login, I store the access_token in SecureStore using expo.
Now, upon the successful login I need to navigate away from the current stack onto the new one.
What would be the best way to do this, can you return a promise from a redux action? If so would the best way to be inside of the component and then inside of the component do something like
this.props.login(username, password).then(() => { this.props.navigation... });
Or would you do it inside of the action?
A recommended way as below:
componentDidUpdate(prevProps) {
if(this.props.loggedInSuccessfully && !prevProps.loggedInSuccessfully) {
this.props.navigation.navigate....
}
}
onLoginButtonPress = () => {
const { username, password } = this.state;
this.props.login(username, password);
}
Upon successful logged in, update a state loggedInSuccessfully in your reducer and implement logic in componentDidUpdate. This way is the clearest for whoever gonna maintaining your code, in my humble opinion
you can also navigate from redux action or everywhere you want.
read the official docs about navigation service.

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.

Persist user activity in state after reload

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

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