React Component as a prop - reactjs

I was trying to pass a component as prop s to make authenticated routes, but inst working, i tried to search on documentation how to do it or in the web, but unfortunately i didnt found.
So heres is my component
import { useSelector } from "react-redux"
import { Redirect, Route as ReactRoute } from "react-router-dom/cjs/react-router-dom.min"
export const Route = ({isPrivate=false, Component, ...rest}) =>{
const { acessToken } = useSelector(store=>store)
return(
<ReactRoute {...rest} render={()=> isPrivate === !!acessToken ? <Component/> : <Redirect to={isPrivate ? '/' : '/dashboard'}/>} />
)
}
Can anyone give me a hint on this ?

I understood what you are trying to achieve.
function PrivateRoute ({isPrivate=false, component: Component, ...rest}) {
const { acessToken } = useSelector(store=>store)
return (
<Route
{...rest}
render={(props) => isPrivate === !!acessToken
? <Component {...props} />
: <Redirect to={isPrivate ? '/' : '/dashboard'}/>}
/>
)
}
Here is a great example:
How to implement authenticated routes in React Router 4?

when you want to pass component as a prop you can use the under below method
export const Route = (Component) => {
return (props) => {
return <Component {...props} {...resourceProps} />;
};
};

Related

User Context values doesnt change in child component

I am trying to redirect user in case user is not authenticated and vice versa
so, I have the directory structure as follow
myproject
src
App.js
UserContext.js
routes
index.js
route.js
pages
Dashboard
index.js
authentication
login.js
In my app.js i do a call and get my authentication token
and set auth to true and pass it in user context but it has the default values and i cannot redirect currently redirecting with only window.location.href
my code for usercontext.js
import { createContext } from "react";
export const UserContext = createContext(null)
APP.js
const App = props => {
const [user,setUser] = React.useState(null)
var [auth,setAuth] = React.useState(false)
const isAuthenticated = ()=>
{
var isAdmin = true;
axios.get(`/verifyToken`).then((response)=>{
console.log(response.data.auth)
setUser({...response.data.user})
setAuth(response.data.auth)
console.log(response.data.user)
})
}
useEffect(() => {
isAuthenticated()
console.log(auth)
},[]);
function getLayout() {
let layoutCls = VerticalLayout
switch (props.layout.layoutType) {
case "horizontal":
layoutCls = HorizontalLayout
break
default:
layoutCls = VerticalLayout
break
}
return layoutCls
}
const Layout = getLayout()
return (
<React.Fragment>
<Router>
<Switch>
<UserContext.Provider value={{user,setUser,auth,setAuth,isAuthenticated}}>
{publicRoutes.map((route, idx) => (
<Authmiddleware
path={route.path}
layout={NonAuthLayout}
component={route.component}
key={idx}
isAuthProtected={auth}
exact
/>
))}
{authProtectedRoutes.map((route, idx) => (
<Authmiddleware
path={route.path}
layout={Layout}
component={route.component}
key={idx}
isAuthProtected={auth}
exact
/>
))}
</UserContext.Provider>
</Switch>
</Router>
</React.Fragment>
)
}
My index.js file has component and routes names array which i am looping above
and this is my route.js
const Authmiddleware = ({
component: Component,
layout: Layout,
isAuthProtected,
...rest
}) => (
<Route
{...rest}
render={props => {
return (
<Layout>
<Component {...props} />
</Layout>
)
}}
/>
)
Authmiddleware.propTypes = {
isAuthProtected: PropTypes.bool,
component: PropTypes.any,
location: PropTypes.object,
layout: PropTypes.any,
}
export default Authmiddleware;
So, now If in my dashboard.js I try to access user on wan tto redirect if auth is false it only has default values of user and auth
I am fetching as follows in dashboard.js
import {UserContext} from '../../UserContext'
const {user,setUser,auth,setAuth,isAuthenticated} = React.useContext(UserContext)
React.useEffect(()=>{
if(auth == false){
window.location.href='/login'
//IT TAKES ME LOGIN EVERYTIME AT IT IS ONLY GETTING DEFAULT VALUE THAT IS FALSE
},[])
WHAT I HAVE TRIED
If i place the isAuthenticated() function call in every component it works
but that would be like so many lines of code same in every component
What is the way to go with?
Anyone facing the same issue I resolved it by
bringing out
<UserContext.Provider></UserContext.Provider>
outside the switch
<UserContext.Provider value={{user,setUser,auth,setAuth,isAuthenticated}}>
<Switch>
</Switch>
</UserContext.Provider value={{user,setUser,auth,setAuth,isAuthenticated}}>
I FOUND THE REASON HERE: https://coderedirect.com/questions/324089/how-to-use-context-api-with-react-router-v4
The reason posted in answer here was that Switch expects routes directly.

I'm not able to get the value of state:{from:props.location} in Login.js

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

How to pass state in history.push in React-Router

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

Prevent user from directly accessing URL in React application?

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

I can't implement Redirect in React

I want to redirect to the home page when some condition returns null or false but the action of Redirect is not triggered.
import { Link, Redirect } from "react-router-dom";
if(localStorage.getItem("example") === null || localStorage.getItem("example") === false){
return <Redirect to="/" />
}
I put this code inside in a simple function triggered in one OnClick and componentDidMount(), but it's not working.
You could use Redirect to home page, based on redirect flag that could be changed by using setState in onClickHandler or handleSubmit.
import { Redirect } from "react-router-dom";
class MyComponent extends React.Component {
state = {
redirect: false
}
handleSubmit () {
if(localStorage.getItem("example") === null || localStorage.getItem("example") === false){
return this.setState({ redirect: true });
}
}
render () {
const { redirect } = this.state;
if (redirect) {
return <Redirect to='/'/>;
}
return <YourForm/>;
}
You need to use the Redirect inside render. It is a React Component which renders and then sends the user to the desired path:
import React, { Component } from "react";
import { Route, Switch, Redirect } from "react-router-dom";
class RootPage extends React.Component {
state = {
isLoggedOut: false
};
onClick = () => {
this.setState({
isLoggedOut: true
});
};
render() {
return (
<div>
{this.state.isLoggedOut && <Redirect to="/logout" />}
<button onClick={this.onClick}>Logout</button>
</div>
);
}
}
const Log = () => <h1>Logout</h1>;
class App extends Component {
render() {
return (
<div>
<nav className="navbar navbar" />
<Switch>
<Route exact path="/" component={RootPage} />
<Route exact path="/logout" component={Log} />
</Switch>
</div>
);
}
}
export default App;
When you click on the logout button it will redirect you to the rootPath.
Here is the Demo: https://codesandbox.io/s/q9v2nrjnx4
Have a look at this example in the official docs.
<Redirect /> should be inside your render method if you use a class component. or if you use a function component it should be in what's returned by it.
Example bellow:
import { Component } from 'react';
const PrivateComponent = (props) => {
return(
localStorage.getItem("example")
? <RandomComponent />
: <Redirect to="/signin" />
)
}

Resources