Is there a way to stop the user from directly accessing a URL on my application? For example, we have a page that is accessed as localhost:3000/scheduling but I want to re-route back to the homepage. I couldn't find many helpful articles that could achieve this. I am using React by the way.
Thanks!
You can do it in many ways, this is just an example :
const location = useLocation();
let history = useHistory();
if(location.state == undefined || location.state == null || location.state == ''){
history.push("/");
}
'/' is by default your home page.
You can check this example:
import React from 'react'
import {
BrowserRouter as Router,
Route,
Link,
Redirect,
withRouter
} from 'react-router-dom'
const fakeAuth = {
isAuthenticated: false,
authenticate(cb) {
this.isAuthenticated = true
setTimeout(cb, 100)
},
signout(cb) {
this.isAuthenticated = false
setTimeout(cb, 100)
}
}
const Public = () => <h3>Public</h3>
const Protected = () => <h3>Protected</h3>
class Login extends React.Component {
state = {
redirectToReferrer: false
}
login = () => {
fakeAuth.authenticate(() => {
this.setState(() => ({
redirectToReferrer: true
}))
})
}
render() {
const { from } = this.props.location.state || { from: { pathname: '/' } }
const { redirectToReferrer } = this.state
if (redirectToReferrer === true) {
return <Redirect to={from} />
}
return (
<div>
<p>You must log in to view the page</p>
<button onClick={this.login}>Log in</button>
</div>
)
}
}
const PrivateRoute = ({ component: Component, ...rest }) => (
<Route {...rest} render={(props) => (
fakeAuth.isAuthenticated === true
? <Component {...props} />
: <Redirect to={{
pathname: '/login',
state: { from: props.location }
}} />
)} />
)
export default function AuthExample () {
return (
<Router>
<div>
<ul>
<li><Link to="/public">Public Page</Link></li>
<li><Link to="/protected">Protected Page</Link></li>
</ul>
<Route path="/public" component={Public}/>
<Route path="/login" component={Login}/>
<PrivateRoute path='/protected' component={Protected} />
</div>
</Router>
)
}
Source
We can use Conditional rendering tracing the history.
You can also add conditions using this.props.history.location.key or this.props.history.action
Key exists and action is 'PUSH' when we redirect user using this.props.history.push
Key property doesn't exist and action is 'POP' when a user tries to access the URL directly
return this.props.history.location.key ? (<div></div>) : null
Related
const PrivateRoute = ({ ...rest }) => {
return (
<Route
{...rest}
render={(props) => {
return props.isAuthPass === true ? (
props.children
) : (
<Redirect to={{ pathname: '/', state: { from: props.location } }} />
);
}}
/>
);
};
---> This is the Login.js code
//let { from } = location.state || { from: { pathname: "/" } };
// const {state} = useLocation()
// Also props.state is not working here
// state is always undefined
if (redirectToReferrer === true) {
return <Redirect to={state?.from || '/'} />;
}
If you wrapped your main App component around BrowserRouter from 'react-router-dom' it should be passed into you your App component and you should be able to access it via props. Check in your main component if you have access to props.location. If you do, you need to pass it down to the component that is trying to access it. Than you need to add the component that requires it with
Here is a link to help props.location in documentation
I am not able to send the parameter through state using useHistory history.push method from react-router dom.
Now suppose I want to pass more than a string to the Paging component i.e. some props too.
My Paging Component which throws error for state value state is not defined
const PAGING = ({ location }) => {
console.log(location);
console.log(location.state);
console.log(location.state.id);
return <div>Hello <div>}
History.push method in another component
const handleDetails = (id,name) => {
console.log(name)
if (id) {
return history.push({
pathname: `/detailing/${name}`,
state: { id }
});
} else {
return history.push("/");
}
};
const Switch = () => {
const { state: authState } = useContext(AuthContext)
return (
<div>
<Router>
<Switch>
<ProtectedSystem
path= "/detailing/:name"
exact
auth={authState.isAuthenticated}
component={PAGING}
/>
</Switch>
</Router>
</div>
);
const ProtectedSystem = ({auth , component: Component, ...rest}) =>{
return(
<Route
{...rest}
render={() => auth ? (<Component/>) : (<Redirect to = '/' /> )}
/>
)
}
If I use simple route without condition based its working fine
<Route path= "/detailing/:name" exact component={PAGING} />
You need to pass on the Route params to the rendered component so that it can use them
const ProtectedSystem = ({auth , component: Component, ...rest}) =>{
return(
<Route
{...rest}
render={(routeParams) => auth ? (<Component {...routeParams}/>) : (<Redirect to = '/' /> )}
/>
)
}
You can do this entirely with React hooks and pure functions, eg.
import React from 'react';
import { useHistory } from 'react-router-dom';
const ProtectedSystem = ({ auth }) => {
const history = useHistory();
if (!authUser) {
history.push("/signin");
}
return (
<div><h1>Authorized user</h1></div>
)
}
export default ProtectedSystem
I'm setting up a basic authentication system with React and while signup and login actions correctly redirect and render the appropriate components, my logout action redirects to the protected route and renders the associated component, even though the authentication variable managed with the context API is successfully updated when logging out. The whole operation works in the end, as when I'm refreshing the page, I am successfully redirected to my login page.
I'm using Node.js to manage my sessions and dispatching the logout action works well as, as I said, the variable used with the Context API is updated. I'm using the Effect Hook on my Header component where the logout is initiated and I can see the auth variable being changed.
Here is my code:
AppRouter.js
export const history = createBrowserHistory();
const AppRouter = () => (
<Router history={history}>
<Switch>
<PublicRoute path="/" component={AuthPage} exact={true} />
<PrivateRoute path="/dashboard" component={DashboardPage} />
<Route component={NotFoundPage} />
</Switch>
</Router>
);
PublicRoute.js
const PublicRoute = ({ component: Component, ...rest }) => {
const { uid } = useContext(AuthContext);
useEffect(() => {
console.log("Public Route - Variable set to:", uid);
}, [uid])
return (
<Route
render={props =>
uid !== undefined ? (
<Redirect to="/dashboard" />
) : (
<Component {...props}/>
)
}
{...rest}
/>
)
};
PrivateRoute.js
const PrivateRoute = ({ component: Component, ...rest }) => {
const { uid } = useContext(AuthContext);
useEffect(() => {
console.log("Private Route - Variable set to:", uid);
}, [uid])
return (
<Route
render={props =>
uid !== undefined ? (
<div>
<Header />
<Component {...props}/>
</div>
) : (
<Redirect to="/" />
)
}
{...rest}
/>
)
};
Header.js
export const Header = () => {
const { uid, dispatch } = useContext(AuthContext);
useEffect(() => {
console.log("Header - Variable set to:", uid);
// console.log("HIST", history);
}, [uid])
const logout = async () => {
const result = await startLogout();
if (result.type !== undefined) {
dispatch(result); // Works well
// window.location.href = '/';
// history.push('/');
history.replace('/');
} else {
console.log(result);
}
}
return (
<header className="header">
<div className="container">
<div className="header__content">
<Link className="header__title" to="/dashboard">
<h1>A React App</h1>
</Link>
<button className="button button--link" onClick={logout}>Logout</button>
</div>
</div>
</header>
);
};
I tried both history.push('/') and history.replace('/'). Both these 2 methods work well as if I switch the path to an unknown route, my component that handles 404 is successfully rendered.
Below is my console output when I click the logout button. As you can see, the auth variable is well updated to undefined but that does not prevent my router to keep showing me the protected route. The router should not redirect me to the dashboard as my auth variable is set to undefined after logging out.
Header - Variable set to: {uid: undefined}
Private Route - Variable set to: {uid: undefined}
Public Route - Variable set to: {uid: undefined}
Header - Variable set to: {uid: undefined}
Private Route - Variable set to: {uid: undefined}
For the time being I'm using window.location.href = '/'; which works well, as it automatically reload the root page but I'd like to stick to react-router. Any thoughts? Thanks
in the private route pass renders props.. like this:
const PrivateRoute = ({ component: Component, ...rest }) => {
const { uid } = useContext(AuthContext);
useEffect(() => {
console.log("Private Route - Variable set to:", uid);
}, [uid])
return (
<Route
render={props =>
uid !== undefined ? (
<div>
<Header {...props} />
<Component {...props}/>
</div>
) : (
<Redirect to="/" />
)
}
{...rest}
/>
)
};
then in header use props to push history:
export const Header = (props) => {
const { uid, dispatch } = useContext(AuthContext);
useEffect(() => {
console.log("Header - Variable set to:", uid);
// console.log("HIST", history);
}, [uid])
const logout = async () => {
const result = await startLogout();
if (result.type !== undefined) {
dispatch(result); // Works well
// window.location.href = '/';
// history.push('/');
props.history.push('/');
} else {
console.log(result);
}
}
I want to build a membership-based web app using React. The users would sign-up and pay before they can access exclusive content. If they have already registered or signed up they can just login and access the content.
How would I go about making such a web app with React? What would I need to implement?
You can use react-router, found in npm packages : https://www.npmjs.com/package/react-router
See this example to private routes https://reacttraining.com/react-router/web/example/auth-workflow
import React from "react";
import {
BrowserRouter as Router,
Route,
Link,
Redirect,
withRouter
} from "react-router-dom";
////////////////////////////////////////////////////////////
// 1. Click the public page
// 2. Click the protected page
// 3. Log in
// 4. Click the back button, note the URL each time
const AuthExample = () => (
<Router>
<div>
<AuthButton />
<ul>
<li>
<Link to="/public">Public Page</Link>
</li>
<li>
<Link to="/protected">Protected Page</Link>
</li>
</ul>
<Route path="/public" component={Public} />
<Route path="/login" component={Login} />
<PrivateRoute path="/protected" component={Protected} />
</div>
</Router>
);
const fakeAuth = {
isAuthenticated: false,
authenticate(cb) {
this.isAuthenticated = true;
setTimeout(cb, 100); // fake async
},
signout(cb) {
this.isAuthenticated = false;
setTimeout(cb, 100);
}
};
const AuthButton = withRouter(
({ history }) =>
fakeAuth.isAuthenticated ? (
<p>
Welcome!{" "}
<button
onClick={() => {
fakeAuth.signout(() => history.push("/"));
}}
>
Sign out
</button>
</p>
) : (
<p>You are not logged in.</p>
)
);
const PrivateRoute = ({ component: Component, ...rest }) => (
<Route
{...rest}
render={props =>
fakeAuth.isAuthenticated ? (
<Component {...props} />
) : (
<Redirect
to={{
pathname: "/login",
state: { from: props.location }
}}
/>
)
}
/>
);
const Public = () => <h3>Public</h3>;
const Protected = () => <h3>Protected</h3>;
class Login extends React.Component {
state = {
redirectToReferrer: false
};
login = () => {
fakeAuth.authenticate(() => {
this.setState({ redirectToReferrer: true });
});
};
render() {
const { from } = this.props.location.state || { from: { pathname: "/" } };
const { redirectToReferrer } = this.state;
if (redirectToReferrer) {
return <Redirect to={from} />;
}
return (
<div>
<p>You must log in to view the page at {from.pathname}</p>
<button onClick={this.login}>Log in</button>
</div>
);
}
}
export default AuthExample;
I am new to SPA development. I use React and React Router and made simple app. It has two pages: public and protected. User can see protected page only when he was signed in. I use firebase for managing users.
The problem is when I go to protected page, log in and can see its content, I then resresh the page and redirected to "default" state which is public page.
index.js
ReactDOM.render(<App />, document.getElementById('root'));
App.js
const ProtectedPage = () => {
return (
<div>
<h1> Protected page. </h1>
<Link to="/">Public resources</Link>
</div>
);
}
const PublicPage = () => {
return (
<div>
<h1> Public page. </h1>
<p> Login to see protected resources </p>
<Link to="/protected">Protected resources</Link>
</div>
);
}
const PrivateRoute = ({ component: Component, isAuthenticated, ...rest }) => (
<Route {...rest} render={props => (
isAuthenticated ? (
<Component {...props}/>
) : (
<Redirect to={{
pathname: '/',
state: { from: props.location }
}}/>
)
)}/>
)
class App extends React.Component {
constructor(props) {
super(props);
this.login = this.login.bind(this);
this.logout = this.logout.bind(this);
this.state = {
username: '',
uuid: ''
}
}
login() {
auth.signInWithEmailAndPassword(email, password).then((user) => {
console.log('Sign in ', user.displayName);
this.setState({
username: user.displayName,
uuid: user.uuid
});
});
}
logout() {
auth.signOut().then(() => {
console.log('Sign out ');
this.setState({
username: '',
uuid: ''
});
})
}
componentDidMount() {
this.releaseFirebaseAuthHandler = auth.onAuthStateChanged((user) => {
if(user) {
this.setState({
username: user.displayName,
uuid: user.uuid
});
}
});
}
componentWillUnmount() {
this.releaseFirebaseAuthHandler();
}
render() {
return (
<div>
<div className="navbar">
<nav className="navbar-nav">
{ this.state.username ?
<button onClick={this.logout}>Logout</button> :
<button onClick={this.login}>Login</button> }
</nav>
</div>
<div>
<div>
<Route exact path="/" component={PublicPage} />
<PrivateRoute path="/protected" component={ProtectedPage} isAuthenticated={this.state.uuid !== ''} />
</div>
</div>
</div>
);
}
}
export default App;
Is it possible to preserve localhost:3000/protected page after refreshing it without involving the server?
In your code you redirect to pathname: '/' if user is not authenticated:
const PrivateRoute = (...) => (
<Route {...rest} render={props => (
isAuthenticated
? <Component {...props}/>
: <Redirect to={{
pathname: '/',
state: { from: props.location }
}}/>
)}/>
)
If you would like to keep /protected in url one way to do it is to replace redirect with something like <div>Please login</div>
The state is not preserved across multiple sessions, for this purpose you can use the localStorage.