I have a specific question about coding practices for React.js.
I have two pages in my React application that render UI cards based on data returned from the backend. They are almost identical, except one has a UI for filter options and another has a search bar.
What would be the better way of structuring these two different pages and why?
----------------
OPTION A (one big component with conditional rendering)
App.jsx
<Router>
<Routes>
<Route path="/page1" element={<Page showFilter=true showSearchBar=false />} />
<Route path="/page2" element={<Page showFilter=false showSearchBar=true />} />
</Routes>
</Router>
Page.jsx
function Page(props) {
return (
<>
<Header />
{props.showSearchBar && (<SearchBar />)}
{props.showFilter && (<Filter />)}
<ItemsList />
<Footer />
</>
)
}
----------------
OPTION B (two separate components with sub-component reuse)
App.jsx
<Router>
<Routes>
<Route path="/page1" element={<Page1 />} />
<Route path="/page2" element={<Page2 />} />
</Routes>
</Router>
Page1.jsx
function Page1(props) {
return (
<>
<Header />
<Filter />
<ItemsList />
<Footer />
</>
)
}
Page2.jsx
function Page2(props) {
return (
<>
<Header />
<SearchBar />
<ItemsList />
<Footer />
</>
)
}
----------------
Of course the above list of options isn't exhaustive. If there are any other ways to solve this that might be more suitable for my task, I'd be happy to hear it.
Thanks!
Option C React Composition
App.jsx
<Router>
<Routes>
<Route path="/page1" element={<Page><Filter /></Page>} />
<Route path="/page2" element={<Page><SearchBar /></Page>} />
</Routes>
</Router>
Page.jsx
function Page(props) {
return (
<>
<Header />
{props.children}
<ItemsList />
<Footer />
</>
)
}
I think option A seems fine also, at least until the UI start to differ significantly then you might have to go for option B.
These are my public routes
export const PublicRoutes = () => {
return (
<Switch>
<Route path="/" exact component={Login} />
<Route path={constant.component.logout.url} exact={true} component={Logout} />
<Route path={constant.component.register.url} exact= {true} component={SignUp} />
<Route path={constant.component.forgotPassword.url} exact={true} component={ForgotPassword} />
</Switch>
)
}
This is my index.js
ReactDOM.render(
<React.StrictMode>
<AlertProvider template={AlertTemplate} {...options}>
<Router>
<App />
</Router>
</AlertProvider>
</React.StrictMode>,
document.getElementById('root')
);
This is my app.js where I am trying to render routes based on accessToken in the local storage
return (
<>
{token ? <DashboardLayout /> : <PublicRoutes />}
</>
)
And finally this my dashboard layout where I want the authenticated routes to be shown to the right of the sidebar
<>
<SideBar>
<div className="w-100">
<Switch>
<Route path="/dashboard" component={Dashboard} />
<Route path="/my-account" component={Account} />
</Switch>
</div>
<Footer>
</>
Now, when I login, it redirects me to "/dashboard" the url changes but the page shows an empty screen and it works if I refresh the page manually. What am I doing wrong? Thanks
Are you sure your token is not empty right after you login ? Maybe your token is empty at this moment, PublicRoutes is rendered and that's why the /dashboard route display an empty screen. Maybe your token only gets value after refresh.
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.
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.
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={ }