I'am nesting routes in my project. I have App.js in which I have defined the routes and inside the component I have more routes which I want them to be nested. The only problem is that my nested route is in the component which is connected to redux. The nested routing is not working properly.
I have already tried it from the official documentation but it does not work.
https://reacttraining.com/react-router/core/guides/philosophy
App.js
import { connect } from "react-redux";
import { withRouter, Route } from "react-router-dom";
function HowItWorks() {
return (
<div>
<h2 style={{ margin: 20 }}>How It Works</h2>
</div>
);
}
function AboutUs() {
return (
<div>
<h2 style={{ margin: 20 }}>About Us</h2>
</div>
);
}
class App extends React.Component {
render() {
return (
<div>
<Route path="/" exact component={HowItWorks} />
<Route path="/howitworks" exact component={HowItWorks} />
<Route path="/aboutus" component={AboutUs} />
<Route path="/admin" component={AdminContainer} />
</div>
);
}
}
Below is my Redux Container file which gets called based on the route specified in App.js. Also my App.js file may get connected to redux in the future by the connect() method.
AdminContainer.js
import { connect } from "react-redux";
import MainDesktopComponent from "../components/Admin/MainDesktopComponent";
const mapStateToProps = state => ({});
const mapDispatchToProps = dispatch => {
return {};
};
export default connect(
mapStateToProps,
mapDispatchToProps
)(MainDesktopComponent);
MainDesktopComponent.js
I have tried this i.e giving the nested route inside Switch and many different ways but it is not working. Also note that I also want to pass props to the Dashboard component which will come from the above redux container component through mapstatetoprops.
import React from "react";
import Dashboard from "./Dashboard";
import { Route } from "react-router-dom";
import { Switch } from "react-router";
function MainDesktopComponent(props) {
return (
<div>
<Switch>
<Route
exact
path="/admin/dashboard"
render={props => {
<Dashboard/>;
}}
/>
</Switch>
</div>
);
}
export default MainDesktopComponent;
I'm not sure but what about try this?
<Switch>
<Route
exact
path="/admin/dashboard"
render={cProps => <Dashboard {...cProps} {...props} />}
/>
</Switch>
return Route render component.
Related
I'm using MUIv5 and React-Router v6 in my project, in which I want to wrap a layout around my pages, but the pages aren't rendering and all i'm getting is an empty div
This is my App component:
import React from "react";
import { Routes, Route } from "react-router-dom";
import { CssBaseline } from "#mui/material";
import MainLanding from './routes/MainLanding';
import StoreLanding from "./routes/StoreLanding";
import Layout from "./components/Layout";
const App = () =>{
return(
<>
<CssBaseline/>
<Routes>
<Route element={<Layout/>}>
<Route path="/" element={<MainLanding/>}/>
<Route path="/store" element={<StoreLanding/>}/>
</Route>
</Routes>
</>
)
}
export default App
This is the layout component where i'm calling the children via props:
import React from 'react';
const Layout = ({ children }) => {
return (
<div>
{children}
</div>
)
}
export default Layout;
Output:
A layout component should render an Outlet for nested Route components to be rendered into. This is different from wrapper components that consume and render the children prop.
See Layout Routes and Outlet for more details.
import { Outlet } from 'react-router-dom';
const Layout = () => {
return (
<div>
<Outlet /> // <-- nested routes rendered here!
</div>
)
};
For comparison, wrapper components are used to wrap a child component in the element prop. Example:
<Routes>
<Route
path="/"
element={(
<Layout>
<MainLanding />
</Layout>
)}
/>
<Route
path="/store"
element={(
<Layout>
<StoreLanding />
</Layout>
)}
/>
</Routes>
Hi I have a react (GatsbyJs) app where i am using dynamically fetched data and authentication. I have a PrivateRoute component that checks whether the user is logged in and then redirects to the component or to the login page depending on the status.
I now need to pass props to components but couldn't master it.
Here is the PrivateRouter:
import React from 'react'
import { navigate } from 'gatsby'
import { isLoggedIn } from '../services/auth'
const PrivateRoute = ({ component: Component, location, ...rest }) => {
if (!isLoggedIn() && location.pathname !== '/app/login') {
navigate('/app/login')
return null
}
return <Component {...rest} />
}
export default PrivateRoute
And the app.js code:
import React from "react"
import { Router } from "#reach/router"
import Layout from "../components/layout"
import OrderDetails from "../modules/order-details"
import ItemDetails from "../modules/item-details"
import ProductionOrders from "../modules/production-orders"
import ProdOrderDetail from "../modules/production-order-detail"
import CardDetail from '../modules/card-details'
import Cards from "../modules/cards"
import Orders from "../modules/orders"
import Items from "../modules/items"
import PrivateRoute from '../components/privateRoute'
import Profile from '../components/profile'
import Login from '../modules/login'
import ForgotPassword from "../modules/forgotPassword"
import NewPassword from "../modules/newPassword"
import Invoices from "../modules/invoices"
import Dispatches from "../modules/dispatches"
import InvoiceDetails from "../modules/invoice-details"
import OrderPlan from "../modules/order-plan"
import AccountStatementPage from "../modules/acc-statement"
const App = () => {
return (
<Layout>
<Router basepath="/app">
<PrivateRoute path="/order-details/:orderId" component={OrderDetails} />
<PrivateRoute path="/item-details/:itemId" component={ItemDetails} />
<PrivateRoute path='/production-orders' component={ProductionOrders} />
<PrivateRoute path="/production-order-detail/:companyNr/:orderId" component={ProdOrderDetail} />
<PrivateRoute path="/cards" component={Cards} />
<PrivateRoute path="/card-details/:cardId" component={CardDetail} />
<PrivateRoute path="/orders" component={Orders} />
<PrivateRoute path="orders/cId/:cId" component={Orders} />
<PrivateRoute path="orders/keyword/:keyword" component={Orders} />
<PrivateRoute path="/items" component={Items} />
<PrivateRoute path="/items/keyword/:keyword" component={Items} />
<Login path="/login" />
<ForgotPassword path="/forgot-password" />
<NewPassword path="/new-password" />
<PrivateRoute path="/profile" component={Profile} />
<PrivateRoute path="/invoices/" component={Invoices}/>
<PrivateRoute path="/invoices/cId/:cId" component={Invoices}/>
<PrivateRoute path="/dispatches/" component={Dispatches}/>
<PrivateRoute path="/dispatches/cId/:cId" component={Dispatches}/>
<PrivateRoute path="/invoice-details/:invId" component={InvoiceDetails} />
<PrivateRoute path="/order-plan" component={OrderPlan} />
<PrivateRoute path="/acc-statement/:id" component={AccountStatementPage}/>
</Router>
</Layout>
)
}
export default App
How should I rework them to be able to pass down props to the Component?
Thanks in advance.
P.S.
This is the component:
import React from 'react'
import Container from "react-bootstrap/Container"
import Col from 'react-bootstrap/Col'
import Row from 'react-bootstrap/Row'
import AccStatement from '../components/accStatement'
const AccountStatementPage = (props,{location}) => {
console.log(location)
console.log(props)
return (
<Container fluid>
<h1>Cari Hesap Ekstresi</h1>
<Row className="h-100">
<AccStatement id={props.id} />
</Row>
</Container>
)
}
export default AccountStatementPage
Your private route component is already configured to pass on any additional props passed to it from the router, but since you are trying to send additional data to the rendered component you need to send the route state correctly.
Link
<Link to=“...” myState=“....” >
You can access route state from the location prop passed to each rendered component.
const MyComponent = ({ location }) => {
return (
<div>My route state: {location.state.myState}</div>
);
};
If your component doesn't receive the route props then you can use the useLocation react hook.
const MyComponent = () => {
const location = useLocation();
return (
<div>My route state: {location.state.myState}</div>
);
};
How should I access it in the target component?
location should be injected into the props object passed to your component by the Route. It isn't a separate argument. The route params are also placed on the match prop and are not a root-level prop value.
Given
<PrivateRoute
path="/acc-statement/:id"
component={AccountStatementPage}
/>
Component
const AccountStatementPage = ({ location, match }) => {
useEffect(() => {
// Log route state and match param `id` on mount
console.log(location.state, match.id);
}, []);
return (
<Container fluid>
<h1>Cari Hesap Ekstresi</h1>
<Row className="h-100">
<AccStatement id={match.id} />
</Row>
</Container>
)
};
You're really close.
This is working for me. Pass your state object inside Link per the Gatsby spec. Then you need to access your Router props (which contains the location object which has your Link state) and pass location.state.example to a component like so.
Link
<Link
className="link-wrapper"
to={endpoint}
state={{ example: 'something' }}
>
RouterPage (PrivateRouter)
const RouterPage = (
props: { pageComponent: JSX.Element } & RouteComponentProps
): JSX.Element => {
if (!loggedIn) {
navigate('/404')
}
return props.pageComponent
}
Router
const App: React.FC<RouteComponentProps> = (props: any) => {
return (
<Router className="below-header-container">
<RouterPage
path={withPrefix('/some-path/create')}
pageComponent={<CreateSomething />}
/>
<RouterPage
path={withPrefix('/some-path/:id')}
pageComponent={
<EditSomething name={props.location.state.example} />
}
/>
<RouterPage
path={withPrefix('/some-path')}
pageComponent={<Something />}
/>
</Router>
)
}
Component
const EditSomething: React.FC<any> = ({ name }) => {
return (
<section className="edit-something">
<div className="row">
<h1>{name}</h1>
</div>
</section>
)
}
In your PrivateRoute component you are destructuring the component as a Component (to render it), a location, and the rest (...rest) for rest of the props.
To pass a custom prop to your component inside PrivateRoute, you just need to access directly your prop:
In your app.js:
<PrivateRoute path="/order-plan" component={OrderPlan} customProp={`hello`}/>
Your customProp is passing to your component through rest spread operator in PrivateRoute so, in your component, just access to props.customProp.
If you want to pass something using Link component, you should use the state provided by Gatsby Link itself (because Gatsby extends from #reach/router):
<Link
to={`/order-plan`}
state={{ yourVariable: 'hello' }}
>
Then, on your order-plan page you have to access to props.location.state.yourVariable to get your data.
I couldn't access the route state through location. It was undefined all the time.
But I could access the route state through:
window.history.state.customProps
Thanks for your support, be well.
I am trying to redirect from my context following a failed update of the state from the a cookie.
import React, { createContext, Component } from 'react';
import { withRouter } from "react-router-dom";
import Cookies from 'universal-cookie';
export const MyContext = createContext();
const cookies = new Cookies();
class MyProvider extends Component {
componentDidMount() {
this.setStateFromCookie();
}
setStateFromCookie = () => {
try {
this.setState({ data: cookies.get('my-cookie')['data'] });
} catch(error) {
console.log(error);
this.props.history.push('/');
}
return
};
render() {
return (
<MyContext.Provider value={{...this.state}}>
{this.props.children}
</MyContext.Provider>
);
}
}
export default withRouter(MyProvider);
I am using a withRouter hook to this.props.history.push('/'), becuase the context is wrapping the router
class MyApp extends Component {
render() {
return (
<BrowserRouter>
<MyProvider>
<div className="MyApp">
<Router>
<Route exact path='/' component={Index} />
<Route exact path='/dashboard' component={Dashboard} />
</Router>
</div>
</MyProvider>
</BrowserRouter>
);
}
}
export default MyApp;
The problem is that the redirect to the home page following the error, but the home page isn't rendering.. I still see the dashboard page.
Any idea what is going on and how to fix this
The issue is that you have a nested Router wrapping your Routes. You need to remove that and then everything will work fine
<BrowserRouter>
<MyProvider>
<div className="MyApp">
<Route exact path='/' component={Index} />
<Route exact path='/dashboard' component={Dashboard} />
</div>
</MyProvider>
</BrowserRouter>
When you use a nested Router, and try to navigate from Provider, the history used by Provider is being provided by BrowserRouter and hence it isn't able to communicate to the Routes whcih are dependent on the inner <Router> component for history.
Using a single router wrapping your components solves this issue
I'm fairly beginner in react/redux, and I'm creating a simple project using React, Redux and React Router v4. I use Redux to handle the state and it sucessfully passes states to root component Wrapper, but it seems it doesn't pass it to Home or other components.
When I console log this.props.Gallery in Wrapper, the data displays but when I do it on Gallery, it shows 'undefined'. I've passed children in Wrapper using React.cloneElement but it didn't work. Is there an explanation to this and some workaround?
My app project structure is
--wrapper
-home
-gallery
-about
-links
Here are the components
For Routing
App.js
import React from 'react'
import ReactDOM from 'react-dom'
import { BrowserRouter, Route, Switch } from 'react-router-dom'
import Wrapper from './components/Wrapper';
...
//import store
import {Provider} from 'react-redux'
import store , {history} from './store'
//router
const mainRouter=(
<Provider store={store} history={history}>
<Wrapper>
<BrowserRouter>
<Switch>
<Route exact path='/' component={ Home } />
<Route path='/portfolio' component={ Gallery } />
<Route path='/about' component={ About } />
<Route path='/links' component={ Links } />
</Switch>
</BrowserRouter>
</Wrapper>
</Provider>
)
ReactDOM.render(mainRouter, document.getElementById('root'))
Wrapper
import React from 'react'
import {bindActionCreators} from 'redux'
import {connect} from 'react-redux'
import * as actionCreators from '../actions/actionCreators'
function mapStateToProps(state){
return{
gallery:state.gallery
}
}
function mapDispatchToProps(dispatch){
return bindActionCreators(actionCreators,dispatch)
}
class Wrapper extends React.Component{
render(){
return(
<div>
{React.cloneElement(this.props.children, this.props)}
</div>
)
}
}
export default connect(
mapStateToProps,
mapDispatchToProps
)(Wrapper)
Home.js
import React from 'react'
import { NavLink } from 'react-router-dom'
//import component
...
class Home extends React.Component {
render(){
console.log(this.props.gallery)
return(
<div>
<p>this is home page</p>
<Header/>
<Nav/>
<Footer/>
</div>
)
}
}
export default Home;
Update 9/19
I've managed to created nested components in react router v4, but still unable to pass props to its direct children. It throws an error of children being undefined
And i've also updated my component structure to
--Home ~ this is more like a welcome containing app's menu
--Wrapper
-gallery
-about
-links
Here is what I did: I've moved Wrapper to be wrapper of gallery, about and links
App.js
<Provider store={store} history={history}>
<BrowserRouter>
<div>
<Route exact path="/" component={Home}/>
<Route path="/h" component={Wrapper}/>
</div>
</BrowserRouter>
</Provider>
Wrapper.js
function mapStateToProps(state){
return{
gallery:state.gallery
}
}
function mapDispatchToProps(dispatch){
return bindActionCreators(actionCreators,dispatch)
}
class HomePage extends React.Component {
render() {
return(
<div>
<Route path={`${this.props.match.path}/portfolio`} component={ Gallery } />
<Route path={`${this.props.match.path}/about`} component={ About } />
<Route path={`${this.props.match.path}/links`} component={ Links } />
</div>
)
}
}
export default connect(
mapStateToProps,
mapDispatchToProps
)(Wrapper)
You are correctly passing the props from Wrapper down to it's children. However, Home and Gallery are not it's children. They are great great grand children. The only child of Wrapper is the element(s) directly within it. In this case, that is BrowserRouter.
There are a great number of ways that you could make these props available to Home and Gallery. But passing directly from Wrapper is not one of them.
The simplest way would be to make a GalleryContainer class. This would be identical to the Wrapper class you already created (include it's connections to redux), except that it would explicitly render Gallery, as such;
render() {
return <Gallery {...this.props}/>;
}
Then use this new GalleryContainer as the component for the /portfolio route.
I finally managed to pass the props to child components.
I created two containers for my project, one as main container and other as parent to sub components Gallery, About and Links.
Here is structure in App.js
const mainRouter=(
<Wrapper>
<Provider store={store} history={history}>
<BrowserRouter>
<div>
<Route exact path="/" component={Home}/>
<Route path="/h" component={Container}/>
</div>
</BrowserRouter>
</Provider>
</Wrapper>
)
Then in Container, I connected it to redux, and created route to its sub children components and used render to render the components and pass gallery props to Gallery
<Route path={`${this.props.match.path}/portfolio`} render={()=><Gallery {...this.props.gallery}/>}/>
Here is the Container
import React from 'react'
import { bindActionCreators } from 'redux'
import { connect } from 'react-redux'
import * as actionCreators from '../actions/actionCreators'
import { Route } from 'react-router-dom'
//import components
...
function mapStateToProps(state) {
return {
gallery: state.gallery
}
}
function mapDispatchToProps(dispatch) {
return bindActionCreators(actionCreators, dispatch)
}
class Container extends React.Component {
render() {
return (
<div>
<ResponsiveNav/>
<main>
<Route path={`${this.props.match.path}/portfolio`}
render={ ()=><Gallery{...this.props}/> } />
<Route path={`${this.props.match.path}/links`}
render={ ()=><Link{...this.props}/> } />
<Route path={`${this.props.match.path}/about`}
render={ ()=><About{...this.props}/> } />
</main>
<Footer/>
</div>
)
}
}
export default connect(
mapStateToProps,
mapDispatchToProps
)(Container)
It seems my application will not render the component passed to <Route /> unless I refresh the page. What could I be doing wrong?
components/App/index.jsx
// dependencies
import React from 'react';
import PropTypes from 'prop-types';
import { Provider } from 'react-redux';
import { BrowserRouter as Router } from 'react-router-dom'
// components
import Header from '../Header';
// containers
import SidebarContainer from '../../containers/SidebarContainer';
import MainContainer from '../../containers/MainContainer';
const App = ({store}) => (
<Provider store={store}>
<Router>
<div className="wrapper">
<Header />
<div className="container-fluid container-fluid--fullscreen">
<div className="row row--fullscreen">
<SidebarContainer />
<MainContainer />
</div>
</div>
</div>
</Router>
</Provider>
);
App.propTypes = {
store: PropTypes.object.isRequired,
};
export default App;
containers/MainContainer.jsx
// dependencies
import React, { Component } from 'react';
import { connect } from 'react-redux';
import { Route } from 'react-router-dom'
// components
import Dashboard from '../components/Dashboard';
import List from '../components/List';
// containers
import LoginContainer from './LoginContainer.jsx'
class Main extends Component {
render() {
console.log(this.props)
return(
<div className="wrapper">
<Route exact path="/" component={Dashboard} />
<Route path="/login" component={LoginContainer} />
<Route path="/users" component={List} />
</div>
);
}
}
const mapStateToProps = (state) => {
return {
token: state.authentication.token,
};
};
const MainContainer = connect(mapStateToProps, null)(Main);
export default MainContainer;
So it seems when I click on a <Link to="/users" /> component my path changes to http://localhost:3000/users but the component does not change from Dashboard to List
I'm also noticing that when I console.log this.props from MainContainer I do not see anything related to router such as this.props.location.pathname --perhaps I'm not structuring my application correctly?
After poking around the react-router issues page on github I found this thread: https://github.com/ReactTraining/react-router/issues/4671
It appears as though the redux connect method blocks context which is required by react-router package.
That being said, the fix for this is to wrap all redux connected components that have router components inside with withRouter() like so:
containers/MainContainer.jsx
// dependencies
import React, { Component } from 'react';
import { connect } from 'react-redux';
import { Route, withRouter } from 'react-router-dom' // IMPORT withRouter
// components
import Dashboard from '../components/Dashboard';
import List from '../components/List';
// containers
import LoginContainer from './LoginContainer.jsx'
class Main extends Component {
render() {
console.log(this.props)
console.log(this.context)
return(
<div className="wrapper">
<Route exact path="/" component={Dashboard} />
<Route path="/login" component={LoginContainer} />
<Route path="/users" component={List} />
</div>
);
}
}
const mapStateToProps = (state) => {
return {
token: state.authentication.token,
};
};
// WRAP CONNECT METHOD
const MainContainer = withRouter(connect(mapStateToProps, null)(Main));
export default MainContainer;
I think you have to do little more tweak in your code to make it work. Assuming you use react-router v4, the following should solve your problem.
import { BrowserRouter, Route, Switch } from 'react-router-dom';
<Provider store={store}>
<BrowserRouter>
<div>
<Switch>
<SidebarContainer />
<MainContainer />
</Switch>
</div>
</BrowserRouter>
</Provider>