I have created a layout component called Main which sends user prop to its children component using React.cloneElement(children, { user: 'First Name'}), but unable to pass that user prop to Route component.
index.js
import React from "react";
import ReactDOM from "react-dom";
import { BrowserRouter as Router, Route } from "react-router-dom";
import Main from "./Main.jsx";
import Home from "./Home.jsx";
const App = () => (
<Router>
<Main>
<Route
path="/"
render={props => {
return <Home {...props} />;
}}
/>
</Main>
</Router>
);
const rootElement = document.getElementById("root");
ReactDOM.render(<App />, rootElement);
Main.jsx
import React from "react";
export default props => {
return <div>{React.cloneElement(props.children, { user: "Username" })}</div>;
};
Home.jsx
import React from "react";
export default props => {
const { user } = props;
return <div>User - {user}</div>;
};
But unable to get user prop in Home component. If I do not use Route then it gets passed to Home.
When I do this, then Home receives user prop.
<Main>
<Home />
</Main>
How can I get user prop and send it to component rendered by Route?
Codesandox link of the scenario - https://codesandbox.io/s/kmyrj0zr8o
Well, you have to wrap Route into own component. The idea is to catch props and pass it manually to Route's render.
Here is modified source: https://codesandbox.io/s/zx508v60yl
Wrapping Route (MyRoute.jsx)
import React from "react";
import { Route } from "react-router-dom";
export default class MyRoute extends React.Component {
render() {
var { Component, path, exact, passedProps } = this.props;
return (
<Route
path={path}
exact={exact}
render={props => <Component {...props} {...passedProps} />}
/>
);
}
}
Modifying Main.jsx
import React from "react";
export default props => {
return (
<div>
{React.cloneElement(props.children, {
passedProps: {
user: "Username"
}
})}
</div>
);
};
Then changing Route to MyRoute in index.js
import React from "react";
import ReactDOM from "react-dom";
import { BrowserRouter as Router, Route } from "react-router-dom";
import Main from "./Main.jsx";
import Home from "./Home.jsx";
import MyRoute from "./MyRoute.jsx";
const App = () => (
<Router>
<Main>
<MyRoute path="/" Component={Home} />
</Main>
</Router>
);
const rootElement = document.getElementById("root");
ReactDOM.render(<App />, rootElement);
When you need to pass props to the router component, you need to use the render prop to do that, but since React.cloneElement will actually just pass the props to the Route component, you can write a wrapper around Route, something like
const RouteWrapper = ({render, exact, strict, path, ...rest}) => {
return (
<Route
exact={exact}
strict={strict}
path={path}
render={(function(routerProps) => {
return render(...routerProps, ...rest)
})()}
/>
)
}
Which you can now use like
const App = () => (
<Router>
<Main>
<RouteWrapper
path="/"
render={props => {
return <Home {...props} />;
}}
/>
</Main>
</Router>
);
Related
app.js file code i want to convert it
import Toolbar from "./components/Toolbar";
// import Iqac from "./components/Iqac/iqac";
import React from "react";
import "./styles.css";
import SideDrawer from "./components/sidemenu/SideDrawer";
import BackDrop from "./components/backdrop/BackDrop";
import { Route, Router, Routes } from "react-router-dom";
class App extends React.Component {
state = {
};
drawerToggleClickHandler = () => {
this.setState(prevState => {
return { sideDrawerOpen: !prevState.sideDrawerOpen };
});
};
backDropClickHandler = () => {
this.setState({ sideDrawerOpen: false });
};
render() {
// let sideDrawer;
let backdrop;
if (this.state.sideDrawerOpen) {
// sideDrawer = <SideDrawer />;
backdrop = <BackDrop click={this.backDropClickHandler} />;
}
return (<>
<div className="tool">
<Toolbar drawerToggleClickHandler={this.drawerToggleClickHandler} />
<SideDrawer show={this.state.sideDrawerOpen} />
{backdrop}
</div>
</>
);
}
}
export default App;
i have tried to change also i want to impliment react router dom v6 so that is why is need to do this and i don't know how to do in react-class based component
import Toolbar from './components/Toolbar'
import Footer from './components/Footer'
import Home from './Pages/Home'
import { useState } from "react";
import About from "./Pages/About"
import {
BrowserRouter as Router,
Route,
Routes
} from "react-router-dom";
const App = () => {
const [state, setstate] = useState({
sideDrawerOpen: false
});
return (
<>
<Router>
<Toolbar />
<Routes>
<Route path='/' element={<Home />}> </Route>
<Route path='/about/*' element={<About />}> </Route>
</Routes>
</Router>
<Footer />
</>
);
}
export default App;
i want to use react router dom and i thing v6 does't work with class
component
please help me resolve this issue i need to lot of your support thankyou so much in advance for the help
Try to change your code like this:
import Toolbar from './components/Toolbar';
import Footer from './components/Footer';
import Home from './Pages/Home';
import { useState } from 'react';
import About from './Pages/About';
import { BrowserRouter as Router, Route, Routes } from 'react-router-dom';
const App = () => {
const [sideDrawerOpen, setSideDrawerOpen] = useState(false);
const drawerToggleClickHandler = () => {
setSideDrawerOpen((prevState) => !prevState);
};
const backDropClickHandler = () => {
setSideDrawerOpen(false);
};
return (
<div className='tool'>
<Toolbar drawerToggleClickHandler={drawerToggleClickHandler} />
<SideDrawer show={sideDrawerOpen} />
{sideDrawerOpen && <BackDrop click={backDropClickHandler} />}
<Router>
<Toolbar />
<Routes>
<Route path='/' element={<Home />}>
{' '}
</Route>
<Route path='/about/*' element={<About />}>
{' '}
</Route>
</Routes>
</Router>
<Footer />
</div>
);
};
export default App;
I'm new to react and is trying out the React.lazy and Suspense imports, and I just have to say, I love them!!! My website went from 45% in performance up to 50-60% and that is without optimizing images! Google search results, here I come!
However, I have a problem, I don't know how to lazy load a component which is rendered in my custom ProtectedRoute and react-router-dom v5.
The lazy loading works and takes effect when I use the React-router-doms native Route, but when I want to load a protected component via one in my custom protected routes, nothing happens, no error message in console or on the website, just a white screen. I suspect there's some problem with the import and code being put in the wrong place.
APP
import React, { Suspense } from "react";
import { BrowserRouter as Router, Route, Switch } from "react-router-dom";
import ProtectedRoute from "./pages/middleware/ProtectedRoute";
const Login = React.lazy(() => import("./pages/Login"));
const WebsiteCRUDs = React.lazy(() => import("./pages/WebsiteCRUDs"));
function App() {
return (
<div className="App">
<Router>
<Switch>
{/* This one works */}
<Suspense fallback={<div>Loading</div>}>
<Route exact path="/admin" component={Login} />
</Suspense>
{/* This one does NOT work */}
<Suspense fallback={<div>Loading</div>}>
<ProtectedRoute exact path="/admin/crud" component={WebsiteCRUDs} />
</Suspense>
</Switch>
</Router>
</div>
);
}
export default App;
ProtectedRoute:
import React from "react";
import { Route, Redirect } from "react-router-dom";
import { useEffect, useState } from "react";
const ProtectedRoute = ({ component: Component, ...rest }) => {
const [isAuth, setIsAuth] = useState(false);
const [isLoading, setIsLoading] = useState(true);
// Logic validation goes here with redirect if user is not auth.
return (
<Route
{...rest}
render={(props) =>
isLoading ? (
<h1>Checking Validation</h1>
) : isAuth ? (
<Component {...props} />
) : (
<Redirect
to={{ pathname: "/admin", state: { from: props.location } }}
/>
)
}
/>
);
};
export default ProtectedRoute;
Please try like this
import React, { Suspense } from "react";
import { BrowserRouter as Router, Route, Switch } from "react-router-dom";
import ProtectedRoute from "./pages/middleware/ProtectedRoute";
const Login = React.lazy(() => import("./pages/Login"));
const WebsiteCRUDs = React.lazy(() => import("./pages/WebsiteCRUDs"));
function App() {
return (
<div className="App">
<Router>
<Switch>
<Suspense fallback={<div>Loading</div>}>
<Route exact path="/admin" component={Login} />
<ProtectedRoute exact path="/admin/crud" component={WebsiteCRUDs} />
</Suspense>
</Switch>
</Router>
</div>
);
}
export default App;
When I change pages, the application is being kept at the same point it was on the previous page. I want to show the component from the top when I change pages. To achieve that, I am trying to implement React Router ScrollToTop.
I found the documentation and implemented it, but I am using react router v6, so it is a bit different.
https://v5.reactrouter.com/web/guides/scroll-restoration
Everything inside the ScrollToTop component doesn't get rendered, and I end up with a blank page.
App.js:
import { Router, Routes, Route } from "react-router-dom";
import './App.scss';
import Main from './pages/Main';
import Projects from './pages/Projects';
import NavBar from './components/NavBar';
import Footer from './components/Footer';
import ScrollToTop from './components/scrollToTop';
function App() {
return (
<div className="app" id="app">
<NavBar />
<div className='app-body'>
<Router>
<ScrollToTop>
<Routes>
<Route path="/portfolio" element={<Main />} />
<Route path="/portfolio/projects" element={<Projects />} />
</Routes>
</ScrollToTop>
</Router>
</div>
<Footer />
</div>
);
}
export default App;
ScrollToTop.js:
import { useEffect } from "react";
import { useLocation } from "react-router-dom";
export default function ScrollToTop() {
const { pathname } = useLocation();
useEffect(() => {
window.scrollTo(0, 0);
}, [pathname]);
return null;
}
As others have pointed out, you are wrapping your Routes component with the ScrollToTop component, but instead of editing it to render its implicit children prop I suggest converting it to a React hook, especially considering since it doesn't actually render anything, you want it to run as a side-effect of navigation.
function useScrollToTop() {
const { pathname } = useLocation();
useEffect(() => {
window.scrollTo(0, 0);
}, [pathname]);
}
...
function App() {
useScrollToTop();
return (
<div className="App">
<div className="app-body">
<NavBar />
<Routes>
<Route path="/portfolio" element={<Main />} />
<Route path="/portfolio/projects" element={<Projects />} />
</Routes>
</div>
</div>
);
}
This necessarily requires you to lift the Router higher in the ReactTree to wrap the App component so it has a routing context to use for the useScrollToTop hook.
const rootElement = document.getElementById("root");
ReactDOM.render(
<StrictMode>
<Router>
<App />
</Router>
</StrictMode>,
rootElement
);
You put the Routes component as a descendant of ScrollToTop component, so you should return children instead of null.
ScrollToTop.js:
import { useEffect } from "react";
import { useLocation } from "react-router-dom";
export default function ScrollToTop({ children }) {
const { pathname } = useLocation();
useEffect(() => {
window.scrollTo(0, 0);
}, [pathname]);
return children;
}
The blank page is because you are returning null from <ScrollToTop> component. Instead if you return <></> or take the {children} prop and return that from <ScrollToTop >, it should work :)
I have a parent component which is rendering some stuff and a child component which is getting some props from parent component. And when user click button in Parent then it should redirect to completely new Page of child componen.
Parent Component
import React from "react";`enter code here`
import "./styles.css";
import { BrowserRouter as Router, Link, Route, Switch } from 'react-router-dom'
import ChildComponent from './ChildComponent'
export default function App() {
const someData = {
name : "Joh Doe"
}
return (
<div className="App">
<h1>This is parent Component</h1>
<Router>
<Link to='/secondpage'>Click me for Second Page</Link>
<Route
path='/secondpage'
render={(props) => (
<ChildComponent {...someData} isAuthed={true} />
)}
/>
</Router>
</div>
);
}
ChildComponent.js
import React from "react";
export default function ChildComponent(props) {
console.log("Data", props);
return <div>This is Second Page. It should open in new page. Also it should render incoming props</div>;
}
Work Demo
try doing this:
import React from 'react';
import {BrowserRouter as Router,Route, Redirect,Switch} from 'react-router-dom';
import Home from './App.js';
import Tutorials from './tutorials.js';
function Routes() {
return (
<Router>
<div>
<Switch>
<Route path="/" component = {Home}>
<Redirect from = "/blog/" to="/tutorials/" />
<Route path = "/tutorials/" component = {About} />
</Switch>
</div>
</Router>
)
}
i am change code .
parent component
import React from "react";
import { Link } from "react-router-dom";
export default function ParentComponent(props) {
console.log("Data", props);
return (
<div className="App">
<h1>This is parent Component</h1>
<Link to="/secondpage">Click me for Second Page</Link>
</div>
);
}
child component
import React from "react";
export default function ChildComponent(props) {
console.log("Data", props);
return (
<div>
This is Second Page. It should open in new page. Also it should render
incoming props
</div>
);
}
app component
import React from "react";
import "./styles.css";
import { BrowserRouter as Router, Route, Switch } from "react-router-dom";
import ChildComponent from "./ChildComponent";
import ParentComponent from "./parentComponent";
export default function App() {
const someData = {
name: "Joh Doe"
};
return (
<div className="App">
<Router>
<Switch>
<Route exact path="/secondpage">
<ChildComponent {...someData} isAuthed={true} />
</Route>
<Route exact path="/" component={ParentComponent} />
</Switch>
</Router>
</div>
);
}
Work Demo
I am trying to make a PrivateRoute component for react. Here is my higher order component. Can you tell me what is the problem with this.
import React from "react";
import { Route, Redirect } from "react-router-dom";
import { connect } from "react-redux";
export default ({ component: Component, ...rest }) => {
class PrivateRoute extends React.Component {
render() {
console.log("This is private route called");
if (this.props.profile) {
return (
<Route
{...rest}
render={props =>
this.props.profile.loggedIn === true ? (
<Component {...props} />
) : (
<Redirect to="/login" />
)
}
/>
);
}
}
}
const mapStateToProps = state => ({
profile: state.profile
});
return connect(mapStateToProps)(PrivateRoute);
};
Here's how you can accomplish a protected route via a protected route component.
Working example: https://codesandbox.io/s/yqo75n896x
containers/RequireAuth.js
import React from "react";
import { Route, Redirect } from "react-router-dom";
import { connect } from "react-redux";
import ShowPlayerRoster from "../components/ShowPlayerRoster";
import ShowPlayerStats from "../components/ShowPlayerStats";
import Schedule from "../components/Schedule";
const RequireAuth = ({ match: { path }, isAuthenticated }) =>
!isAuthenticated ? (
<Redirect to="/signin" />
) : (
<div>
<Route exact path={`${path}/roster`} component={ShowPlayerRoster} />
<Route path={`${path}/roster/:id`} component={ShowPlayerStats} />
<Route path={`${path}/schedule`} component={Schedule} />
</div>
);
export default connect(state => ({
isAuthenticated: state.auth.isAuthenticated
}))(RequireAuth);
routes/index.js
import React from "react";
import { BrowserRouter, Route, Switch } from "react-router-dom";
import { createStore } from "redux";
import { Provider } from "react-redux";
import Home from "../components/Home";
import Header from "../containers/Header";
import Info from "../components/Info";
import Sponsors from "../components/Sponsors";
import Signin from "../containers/Signin";
import RequireAuth from "../containers/RequireAuth";
import rootReducer from "../reducers";
const store = createStore(rootReducer);
export default () => (
<Provider store={store}>
<BrowserRouter>
<div>
<Header />
<Switch>
<Route exact path="/" component={Home} />
<Route path="/info" component={Info} />
<Route path="/sponsors" component={Sponsors} />
<Route path="/protected" component={RequireAuth} />
<Route path="/signin" component={Signin} />
</Switch>
</div>
</BrowserRouter>
</Provider>
);
Or, if you want something that wraps all routes (instead of having to specify a protected route component). Then you can do something like the below.
Working example: https://codesandbox.io/s/5m2690nn6n
components/RequireAuth.js
import React, { Component, Fragment } from "react";
import { withRouter } from "react-router-dom";
import Login from "./Login";
import Header from "./Header";
class RequireAuth extends Component {
state = { isAuthenticated: false };
componentDidMount = () => {
if (!this.state.isAuthenticated) {
this.props.history.push("/");
}
};
componentDidUpdate = (prevProps, prevState) => {
if (
this.props.location.pathname !== prevProps.location.pathname &&
!this.state.isAuthenticated
) {
this.props.history.push("/");
}
};
isAuthed = () => this.setState({ isAuthenticated: true });
unAuth = () => this.setState({ isAuthenticated: false });
render = () =>
!this.state.isAuthenticated ? (
<Login isAuthed={this.isAuthed} />
) : (
<Fragment>
<Header unAuth={this.unAuth} />
{this.props.children}
</Fragment>
);
}
export default withRouter(RequireAuth);
routes/index.js
import React from "react";
import { BrowserRouter, Switch, Route } from "react-router-dom";
import Home from "../components/Home";
import Players from "../components/Players";
import Schedule from "../components/Schedule";
import RequireAuth from "../components/RequireAuth";
export default () => (
<BrowserRouter>
<RequireAuth>
<Switch>
<Route exact path="/" component={Home} />
<Route exact path="/players" component={Players} />
<Route path="/schedule" component={Schedule} />
</Switch>
</RequireAuth>
</BrowserRouter>
);
Or, if you want something a bit more modular, where you can pick and choose any route, then you can create a wrapper HOC. See this example (while it's written for v3 and not for authentication, it's still the same concept).
It looks like your render function's only return is inside of an if block, so its returning null. You need to fix the logic to just return the Route and make profile a required prop in your proptypes check instead of using an if block.
PrivateRoute.propTypes = {
profile: PropTypes.object.isRequired
};