Conditional loading component based on ReactRouter path - javascript

I used a React-Router, coded this
<Router>
<div className="app">
<div className="wrapper">
<AppHeaderMain />
<Switch>
<Route exact path="/" component={Home} />
<Route path="/list/" component={List} />
<Route path="/about/" component={About} />
<Route component={NotFound} />
</Switch>
<div className="menu">
<ul>
<li><NavLink exact to="/">List</NavLink></li>
<li><NavLink to="/about/">About</NavLink></li>
</ul>
</div>
</div>
</div>
</Router>
and I have a question, is it possible to load another component based on Rout path, for example:
path="/" - load <AppHeaderMain />
path="/list/" - load <AppHeaderSubpage />
I know I could insert a AppHeader component into each single component but I would like to don't repeat some additional code which would be required to make that.
I mean something like conditional including (loading) component.
Is it a good practice? Maybe I must to do exactly how I wrote: include in each single component?

I think it is a matter of composition. Why not create a factory function. For example:
function routeHandlerWithAppHeader(Component) {
return (props) => (
<div>
<AppHeaderMain />
<Component {...props}/>
</div>;
);
}
And then use:
<Route exact path="/" component={routeHandlerWithAppHeader(Home)} />

Related

How to hide/show a component inside another component in ReactJS

I have my application looking something like below..
import FirstComponent from "./components/firstComponent";
import NextComponent from "./components/nextComponent";
import MyProgressComponent from "./components/progressComponent";
class App extends React.Component {
render() {
return (
<Router>
<div>
<MyProgressComponent />
<Route path="/" exact component={FirstComponent} />
<Route path="/nextComponent" component={NextComponent} />
</div>
</Router>
);
}
}
As we can see 'MyProgressComponent' is visible when we navigate between 'http://localhost:3000/' and 'http://localhost:3000/nextComponent' because it is directly nested under Router component in App component. But I want 'MyProgressComponent' to be visible only in 'http://localhost:3000/nextComponent' and hidden in 'http://localhost:3000/'. Any suggestion ?
I can do this by importing 'MyProgressComponent' inside each component wherever required but I don't want to duplicate it in each component.
You can render multiple components using the below syntax
<Route path="/nextComponent" render={() =>
<>
<MyProgressComponent />
<NextComponent />
</>
}
/>
Based on #Crocsx comment you can apply following check on your code.
<Router>
<div>
{this.props.location.pathname === "/nextComponent" ? <MyProgressComponent />: null}
<Route path="/" exact component={FirstComponent} />
<Route path="/nextComponent" component={NextComponent} />
</div>
</Router>
you can use switch provided by router to achieve this.
Something like below should work for you.
<Switch>
<Route exact path="/nextComponent" component={MyProgressComponent} />
</Switch>
<Switch>
<Route path="/" exact component={FirstComponent} />
<Route path="/nextComponent" component={NextComponent} />
</Switch>
more documentation is available here https://reacttraining.com/react-router/web/guides/basic-components

React router rendering wrong page

i am developing an web app and I am new to react router. Evrything was going great until I found myself in need to render a whole new page, with new navbar and all.
that's my app.js
class App extends Component {
render() {
return (
<BrowserRouter>
<div className='App'>
<Layout>
<Header />
<NavigationB />
<Search />
<Switch> {/* to swtich to the desired path. Nest all route here */}
<Route path='/' component={Home} exact />
<Route path='/login' component={Login} />
<Route path='/register-choice' component={RegisterButton} />
<Route path='/register-patient' component={RegisterPatient} />
<Route path='/register-professional' component={RegisterProf} />
<Route path='/profesional-dashboard' component={ProfessionalDashboard} />
</Switch>
</Layout>
<Footer />
</div>
</BrowserRouter>
);
}
}
So, I wanted to go to /professional-dashboard but without rendenring all the components above such and Header, Search, etc.
I tried to go to my index.js file and set it up like this
ReactDOM.render(
<BrowserRouter>
<Switch> {/* to swtich to the desired path. Nest all route here */}
<Route path='/' component={App} exact />
<Route path='/professional-dashboard' component=
{ProfessionalDashboard} />
</Switch>
</BrowserRouter>,
document.getElementById('root'));
The idea was, in my form whenever I press register, it should send me to the dashboard of the professional.
At the end of my Register.js file you would find
const WrappedRegistrationForm = Form.create()(RegisterProf);
ReactDOM.render(
<BrowserRouter>
<div>
<WrappedRegistrationForm />
</div>
</BrowserRouter>
, document.getElementById('root'));
export default WrappedRegistrationForm;
I am using Ant Design, so the form renders WrappedRegistrationForm. At first it was not working then I wrapped it around BrowserRouter, I don't get the error anymore, but now when I press the register button, it takes me to /professional-dashboard but it loads app.js and not ProfessionalDashboard.js
Funny thing is, if I refresh the page, it loads ProfessionalDashboard.js normally.
Hope I'm explaining myself well.
Glad if you can help me!!
Hi could try something like this rendering the top one first if its a match if not it will go on to render the rest of the app :) hope this is clear
class App extends Component {
render() {
return (
<BrowserRouter>
<Switch>
<Route exact path='/profesional-dashboard' component={ProfessionalDashboard} />
<StandarRoute path='/' component={MainPage} />
<Switch />
</BrowserRouter>
);
}
}
class MainPage extends Component {
render(){
return(
<div className='App'>
<Layout>
<Header />
<NavigationB />
<Search />
<Switch> {
<Route path='/' component={Home} exact />
<Route path='/login' component={Login} />
<Route path='/register-choice' component={RegisterButton} />
<Route path='/register-patient' component={RegisterPatient} />
<Route path='/register-professional' component={RegisterProf} />
</Switch>
</Layout>
<Footer />
</div>
)
}
}
Try including "exact" before component in your Route, or including "exact" before the 'To' in your Link
Try,
<Route exact path="/register" render={() => ( <Redirect to="/dashboard"/>)
Why do you render two times with reactDOM by the way? That might be what’s causing the issue too. Just exporting it and putting it in route component should suffice.

How to import or use only child components?

For example I have these components
first.js
<div>
<Route path='/' />
<Route path='/first' />
</div>
second.js
<div>
<Route path='/second' />
<Redirect to='/something' />
</div>
And then i have a component that has this
<Switch>
<First>
<Second>
</Switch>
I am using React-Router and Switch does not work if it's child components are not Route. So how does one just strip those divs from first and second components so only things left are the routes?
You can wrap them in fragment like this:
<React.Fragment>
<Route path='/' />
<Route path='/first' />
</React.Fragment>
You can return them in an array:
return [
<Route path='/' key='root' />,
<Route path='/first' key='first' />
]
Note: returning an array is necessary to provide a key props.

React-router deeply nested switch route change re-renders all the way to the root app

I'm using react-router-dom 4+ version and redux in my app.
My route structure is as follows:
index.js:
<BrowserRouter>
<App />
</BrowserRouter>
App.js:
<div className="app">
<Switch>
<Redirect exact from="/" to={redirect} />
<Route path="/auth" component={Auth} />
<Route path="/dashboard" component={RequireAuth(Main)} />
<Route component={NotFound} />
</Switch>
</div>
RequireAuth is HOC that just checks if user is authorized and returns "Main".
Main.js:
<div className="main-page">
<Header url={url} />
<div className="main-container" style={!!this.props.menuOpen ? { marginLeft: '240px' } : { marginLeft: '0px' }}>
<Switch>
<Redirect exact path={url} to={`${url}/tasks`} />
<Route path={`${url}/tasks`} render={() => <h1>TASKS</h1>} />
<Route path={`${url}/done`} render={() => <h1>DONE</h1>} />
<Route path={`${url}/notes`} render={() => <h1>NOTES</h1>} />
<Route path={`${url}/archive`} render={() => <h1>ARCHIVE</h1>} />
<Route component={AltNotFound} />
</Switch>
</div>
</div>
Also Auth.js has similar structure as Main.js, it has a header of sorts and a switch with 2 routes.
Now my question / problem:
Whenever I navigate nested routes like from dashboard/tasks to dashboard/notes etc. (same in Auth.js) all the app re-renders all the way to App.js. I sprinkled console logs in Main.js and App.js componentWillMount, componentDidMount and render methods and they all fire every time. Surely that is not what is supposed to happen, right?
What am I doing wrong and what additional information can I provide here to help people answer? I'm sorry this is my very first question.

Render multiple components in React Router

I'm used to application layouts with multiple yield areas, i.e. for content area and for top bar title. I'd like to achieve something similar in React Router. For example:
<Router>
<Route path="/" component = { AppLayout }>
<Route path="list"
component = { ListView }
topBarComponent = { ListTopBar }/>
</Route>
</Router>
AppLayout:
<div className="appLayout box">
<div className="appLayout topBar">
{ -- display ListTopBar here -- }
</div>
<div className="appLayout content">
{ -- display ListView here -- }
</div>
</div>
Both child components should receive the same props.
How can I approach this?
To passe multiple component you can do like this :
<Route path="groups" components={{main: Groups, sidebar: GroupsSidebar}} />
<Route path="users" components={{main: Users, sidebar: UsersSidebar}}>
See the doc here :
https://github.com/ReactTraining/react-router/blob/v3/docs/API.md#named-components
In v4, according to the docs, you can render multiple components like this:
<Route path='/some-path' render={() =>
<Fragment>
<FirstChild />
<SecondChild />
</Fragment>
} />
Instead of using div's you can use Fragments.
`
<Route path='/some-path' render={props =>
<Fragment>
<Child 1/>
<Child 2/>
</Fragment>
} />
`
You can also use Array in latest versions of React-router-dom;
<Route path="groups" element={[<Component1/>,<Component2/>]} />
Will work just fine.
To render multiple components you can do this:
<Route
path="/EditEmployee/:id"
render={(props) =>
<div>
<NavMenu />
<EditEmployee {...props} />
</div>
}
/>
Here I'm passing parameter to specific conponent.
//this is the simplest method to render multiple components and it works for me
<Router>
<Route path="/">
<ListView />
<ListTopBar />
</Route>
</Router>
<Route path='/' element={<><Header /> <Home /></>} />
This worked for me in the latest react router dom v6
Another method is within the render method of route multiple passed components can be created using react.createElement
<Route render ={(props)=>React.createElement(Component1, {...props}},
React.createElement(Component2, {...props}}/>
What worked for me was to wrap the multiple components in a <Fragment> or a <div> as a parent element.
return (
< Router>
<div className="App" >
<Routes>
<Route path='/'
element={
<Fragment>
< NavBar />
< NewsLetterCard />
< TestimonialsCard />
< ServicesCard />
< ContactsCard />
< Footer />
</Fragment>
}
/>
</Routes>
</div>
</Router>
);
}
For v6, where you are using Routes instead of Switch to render your components. This works:
<Router>
<Routes>
<Route path='/' element={<><Child1/> <Child2/></>} />
</Routes>
</Router>
But for v5, this works:
<Router>
<Switch>
<Route path="/">
<Child1/>
<Child2/>
</Route>
This seem to work for me, I needed to add canvas animation component as background under homepage ("/") only:
import Page from 'pages/Page';
import Blog from 'pages/Blog';
import Post from 'pages/Post';
import Category from 'pages/Category';
import CanvasParticles from 'components/canvas/CanvasParticles';
...
<Routes>
{['/', '/:slug'].map((path, index) => {
return path === '/' ? (
<Route
exact
path={path}
element={[<CanvasParticles />, <Page />]}
key={index}
/>
):(
<Route path={path} element={<Page />} key={index} />
);
})}
<Route exact path="/blog" element={<Blog />}></Route>
<Route path="/blog/:slug" element={<Post />}></Route>
<Route path="/category/:slug" element={<Category />}></Route>
</Routes>
click on this you can view a image v6 feature : this is the simplest method to render multiple components and it works for me
Main Concept is to you should wrap with element
<Route path="/" element={<> </>}
or
wrap with in Fragment
<Route path="/" element={ }

Categories