React router - Navigate inner pages with sub route - reactjs

I am using react routing for navigation among pages. This navigation having child link,
Parent Link 1
Parent Link 2
a. parent1/child link 1
b. parent1/child link 2
a. parent2/child link 1
b. parent2/child link 2
c. parent2/child link 3
How to implement this navigation in react-router.
Please see the attached image with this post to get clear understanding on my query.

class App extends Component {
render() {
return (
<Router>
<div style={{width: 1000, margin: '0 auto'}}>
<ul>
<li><Link to='/'>Home</Link></li>
<li><Link to='/topics'>Topics</Link></li>
</ul>
<hr />
<Route exact path='/' component={Home} />
<Route path='/topics' component={Topics} />
</div>
</Router>
)
}
}
At this point, ” takes in a path and a component. When your app’s current location matches the path, the component will be rendered. When it doesn’t, Route will render null.”
function Topics () {
return (
<div>
<h1>Topics</h1>
<ul>
{topics.map(({ name, id }) => (
<li key={id}>
<Link to={`/topics/${id}`}>{name}</Link>
</li>
))}
</ul>
<hr />
<Route path={`/topics/:topicId`} component={Topic}/>
</div>
)
}
when we go to /topics, the Topic component is rendered. Topics then renders a navbar and a new Route which will match for any of the Links in the navbar we just rendered (since the Links are linking to /topics/${id} and the Route is matching for /topics/:topicId). This means that if we click on any of the Links in the Topics component.
It’s important to note that just because we matched another Route component, that doesn’t mean the previous Routes that matched aren’t still rendered. This is what confuses a lot of people. Remember, think of Route as rendering another component or null. The same way you think of nesting normal components in React can apply directly to nesting Routes.
At this point we’re progressing along nicely. What if, for some reason, another member of your team who wasn’t familiar with React Router decided to change /topics to /concepts? They’d probably head over to the main App component and change the Route
// <Route path='/topics' component={Topics} />
<Route path='/concepts' component={Topics} />
The problem is, this totally breaks the app. Inside of the Topics component we’re assuming that the path begins with /topics but now it’s been changed to /concepts. What we need is a way for the Topics component to receive whatever the initial path as a prop. That way, regardless of if someone changes the parent Route, it’ll always just work. Good news for us is React Router does exactly this. Each time a component is rendered with React Router, that component is passed three props - location, match, and history. The one we care about is match. match is going to contain information about how the Route was matches (exactly what we need). Specifically, it has two properties we need, path and url. These are very similar, this is how the docs describe them -
path - The path pattern used to match. Useful for building nested Routes
url - The matched portion of the URL. Useful for building nested Links
Assume we were using an app that had nested route’s and the current URL was /topics/react-router/url-parameters.
If we were to log match.path and match.url in the most nested component, here’s what we would get.
render() {
const { match } = this.props // coming from React Router.
console.log(match.path) // /topics/:topicId/:subId
console.log(match.url) // /topics/react-router/url-parameters
return ...
}
Notice that path is including the URL parameters and url is just the full URL. This is why one is used for Links and the other used for Routes.
When you’re creating a nested link, you don’t want to use URL paramters. You want the user to literally go to /topics/react-router/url-parameters. That’s why match.url is better for nested Links. However, when you’re matching certain patters with Route, you want to include the URL parameters - that’s why match.path is used for nested Routes.

Related

Refresh page before rendering component in React app

I have similar problem as in Refresh the page only once in react class component.
There are several pages in my application and I move between them using BrowserRouter and useNavigate (react-router-dom v6). One of pages has greater size div and when I go back to main page, it's(main's) css gets messed up(button position changes, some media file grows out of divs, hovers are not displayed) until I refresh page(main page). As soon as I refresh page, everything sets up well.
I used code snippet provided by #rommyarb in the link above. It works, but there is time delay (less 1sec, still visible). Which means when we navigate back(navigate(-1)), it first renders mainpage with broken css --> (0.2-0.5s) then it refreshes and css is recovered.
Time delay is not big, but still it would be unpleasant user experience. Is there any way to first refresh page (localhost/main) then render component with proper css.
Any help would be appreciated!
Code:
function App() {
return (
<Router>
<Routes>
<Route exact path='/' element={<MainPage props ={props}/>} />
<Route path='/UnderConstruction' element={<UnderConstruction/>}/>
</Routes>
</Router>
)
}
function UnderConstruction(props) {
let navigate = useNavigate();
return (
<div className='UnderConstruction' style={somestyles}>
<h2>This page is under construction</h2>
<div style={somestyles}>
<img src={under_construction.jpg'} width="100%" height="60%" />
<Button style={somestyles} onClick={() => {
navigate(-1)
}}> Go Back</Button>
</div>
</div>
);
I solved problem. The makeStyles return function(hook), when we call it within component, we can access only styles naming, which is string. So, every time we call a function, naming changes (makeStyles-element-number) due to global counter. I saved styles in state, which saved only string name. After re-rendering actual styles name was changed(makeStyles-element-number incremented) and the one I saved mismatched with actual styles. The simple solution was not storing makeStyles styles in state.
For more details read: Internal implementation of "makeStyles" in React Material-UI?

ReactJS router + component hierarchy when swapping out components with route changes

TLDR: I'm trying to figure out how to arrange nested routes and my components so that a component can be swapped out based on route.
In SUPER simple terms, I'm trying to build an application where teachers can see the classes they teach at colleges.
The routes of the application are:
/dashboard: call backend to check if the teacher has a default college set on their account, if not then present a college picker dialog where the teacher can select a default college. Once there's a default college (say COLLEGE1), re-route to next route (next bullet point)
/dashboard/college/COLLEGE1: fetch metadata of classes taught in college.
/dashboard/college/COLLEGE1/class/CLASS1: show metadata of a single class within COLLEGE1. This route is accessed by clicking a class in bullet 2.
Here are rough mocks of what this interaction would look like when static (I've colored each component so it's easier for you to refer to them when responding):
However, I am just not able to figure out the nested routes + component hierarchy structure that would get me this.
The hierarchy I have so far is:
<Home>
<Header/>
<!-- content in header -->
</Header>
<MainContent>
<!-- Either loading, for e.g. to fetch permissions -->
<Loading />
<!-- OR display navigation + content -->
<MainContentPage>
<!-- show navigation pane on left, and then choose from report / T and Cs / Contact -->
<Navigation>
<CurrentCollegeInfo />
<DashboardLink />
<TermsAndConditionsLink />
<ContactUsLink />
</Navigation>
<ReportPage>
<!-- Either Loading -->
<Loading />
<!-- OR have user select default college -->
<DefaultCollegePickerPopup />
<!-- OR show college report or class details -->
<CollegeReportPage />
<ClassDetailsPage />
<!-- OR error pages: Not Found, 500, etc. -->
<ErrorPage />
</ReportPage>
<TermsAndConditionsPage />
<ContactUsPage />
</MainContentPage>
</MainContent>
</Home>
How do I insert route management here (I'm using react-router library at the moment btw) so that in the ReportPage component:
either the route is /dashboard (when loading default college from backend or asking user to pick one)
or it is /dashboard/college/COLLEGE1 and fetch college report
or it is /dashboard/college/COLLEGE1/class/CLASS1 and fetch class details?
Or is this not possible and I should rather figure out another flow?
So if I understand correctly, you want to use the react-router to load different components based on which endpoint the user is on? This is 100% possible. You just pass the component you want to show for a specific route as a component property.
You can also use parameters in the paths, so in your example, you have /dashboard/COLLEGE1... I'm assuming you need that to be dynamic to allow for any college. This is done with placing parameters into the path like so... /dashboard/:somevariablename.
<Route
// exact
path={"/dashboard"}
// path={"/dashboard/:collegeId"}
// path={"/dashboard/:collegeId/classes/:classId"}
component={ComponentToPass}
/>
If you make a Route for every possible component/page that the user can visit, and wrap it in a <Switch> component, it will show only one component. You can however skip the <Switch> and add multiple routes to an endpoint as well.
I'm assuming you'll need to use the collegeId and classId in the corresponding components. If you are using functional react, use const { VARNAME } = useParams() to retrieve the parameters you are using. If you are using class-based react, all you need to do is call this.props.match.VARNAME. -- Both are obviously used inside the component that you want to show/use.
So to change your code up a little bit (could be done in a dedicated routes component), heres a light example..
import {HashRouter, Switch, Route} from "react-router-dom"
import DefaultCollegePickerPopup from './wherever'
import CollegeReportPage from './wherever'
import ClassDetailsPage from './wherever'
function RouterComponent(props) {
return (
<HashRouter>
<Switch>
<Route
exact
path={"/dashboard"}
component={DefaultCollegePickerPopup}
/>
<Route
exact
path={"/dashboard/:collegeId"}
component={CollegeReportPage}
/>
<Route
exact
path={"/dashboard/:collegeId/class/:classId"}
component={ClassDetailsPage}
/>
</Switch>
</HashRouter>
)
}
function CollegeReportPage(props) {
const { collegeId } = useParams();
return (
<div>College report for {collegeId}</div>
)
}
class CollegeReportPage extends React.Component {
render() {
const { collegeId } = this.props.match
return (
<div>College report for {collegeId}</div>
)
}
}
If you haven't already looked at this, I would. It gives a LOT of useful information.
https://reactrouter.com/web/guides/quick-start

how to implement website navigation with reactjs

Hi I am developing a website using reactjs. Each page of the website has mainly 3 parts (1. header 2. body 3. footer) . So header and footer will be same for each page and body will keep on changing. Should I create header and footer components and then include them in each page of the website. Is this good design?
How can I highlight navigation menu option for a particular page. For example If I am on contactus page then ContactUs menu option should be highlighted. Similarly If I am one Home Page then "Home" should be highlighted.
In react apps you usually use a router library for this.
A router also takes care of the url in the address bar, so you can save and share links to sub pages in a single page application, and use the browser's back button.
The most popular router for react is called "React Router", but there are other alternatives. It's even possible to write your own router.
React-router's docs has examples of how you can implement this. For the highlighting effect, you can use the component called <NavLink />
Instead of including the header and footer in each page, you start from the outside in. You only put header and footer in once, typically in a main <App />, and then include variable page content inside <Route /> components.
yes you can create 2 components on the top level. they will be header and footer. for navigation; you can use react-router. it will be used to navigate between views. you can put the body component inside your header component your main App structure can be :-
<App>
<HeaderComp/>
<FooterComp/>
</App>
now you can set react-router to change the component being render in body place when any link in the header is clicked. you can also keep the state of currently active view and highlight its color when active.
in react-router v4 you can use switch and route to change between components
<Switch>
<Route exact path='/' component={YourComponent} />
<Route path='/secondcomponent' component={YourSecondComponent} />
<Route path='/thirdcomponent' component={YourthirdComponent} />
</Switch>
this will be your body component , other components like given above will be shown when you click on the link in the head that matches the path in Route tag.
now you header render can be like this.
render(){
return (
<div>
<TopBar/>
<BodyComp/>
<div/>
)
}
the topbar will be fixed and stay on top , the body will have all the space except the margin on top to adjust below the topbar
your topbar can be like this.
render(){
return(
<div className="topBarcontainer">
<Link to="/" >
<div className ="topBarItem">
Home
</div>
</Link>
<Link to="/secondComponent" >
<div className ="topBarItem">
secondComponent
</div>
</Link>
</div>
)
}
as for you want to highlight the current view , you can keep the state array and add give each Link the value from that array , and put onMouseDown on it , when it is clicked it will callback telling the value that is clicked and u will reset all the items background color to default , then you can set the style of that particular active item in your header to active color. you should use inline styling for that instead of className

Why use Link component in React-Router instead of just anchor tag? [duplicate]

I just started on react router.
I have two questions. What is the difference between using <Link to="/page"> and <a href="page">? Both make the exact same get request to /page but I get an error when I use <a href="page"> but it works when I use <Link to="/page"> when I am nesting routes. I don't understand, how there could be any difference, when I know for fact that both render to exact same url?
Second is the weird arrow function in react router v4 documentation
const About = () => (
<div>
<h2>About</h2>
</div>
)
I know () => {} these are new in ES6 but I cannot find anything on normal brackets instead of parentheses. What are they?
Edit
My index.js class (I have all the imports)
render((
<Router>
<div>
<Route component={App}/>
</div>
</Router>
), document.getElementById('root')
);
My App.js class
class App extends Component {
render() {
return (
<div className="container">
<header>
<span className="icn-logo"><i className="material-icons">code</i></span>
<ul className="main-nav">
<li><Link to="/">Home</Link></li>
<li><Link to="/about">About</Link></li>
<li><Link to="/teachers">Teachers</Link></li>
<li><Link to="/courses">Courses</Link></li>
</ul>
</header>
<Route exact path="/" component={Home}/>
<Route path="/about" component={About}/>
<Route path="/teachers" component={Teachers}/>
<Route path="/courses" component={Course}/>
</div>
);
}
}
export default App;
The error I'm getting.
Cannot GET /about on the browser when I try to move to localhost:8080/about. However, when I click the about button, it goes to exactly the same url /about and renders perfectly
This may be a bit late to address your issue and you may well have figured it out. But here's my take:
First:
What is the difference between using <Link to="/page"> and <a
href="page">
On the surface, you seem to be comparing apples and oranges here. The path in your anchor tag is a relative path while that one in the Link is absolute (rightly so, I don't think react-router supports relative paths yet). The problem this creates is say you are on /blah, while clicking on your Link will go to /page, clicking on the <a href='page' /> will take you to /blah/page. This may not be an issue though since you confirmed the correctness of the url, but thought to note.
A bit deeper difference, which is just an addon to #Dennis answer (and the docs he pointed to), is when you are already in a route that matches what the Link points to. Say we are currently on /page and the Link points to /page or even /page/:id, this won't trigger a full page refresh while an <a /> tag naturally will. See issue on Github.
A fix I used to solve my little need around this was to pass in a state property into link like so <Link to={{pathname: "/page", state: "desiredState"}}>Page</Link>. Then I can check for this in the target component's (say <Page />) componentWillReceiveProps like so:
componentWillReceiveProps(nextProps){
if (nextProps.location.state === 'desiredState') {
// do stuffs
}
}
Second question:
the weird arrow function in react router v4 documentation... I cannot find anything on normal brackets instead of parentheses. What are they?
Arrow functions; again #Dennis and #Jaromanda X have kind of addressed it. However, I've got three bits to add:
When you have () => blah without the curly braces {}, you are implicitly returning whatever follows the => in this case blah. But when you have curly braces immediately after the arrow, then it's now your responsibility to return something if you so desire. So () => blah (which by the way is synonymous to () => (blah)) will be more similar to () => { return blah } and not () => { blah }.
So what happens if you want to return an object: { blah: blah }; this is what #Jaromanda X was pointing at. You will then need to do () => ({ blah: blah }) or simply () => ({ blah }) for implicit return or you could return explicitly like so () => { return { blah: blah } }.
My third bit is to point you to MDN
Hope it helps.
The href attribute would trigger a page refresh which would reset the application states. However the link and navlink of react-router doesn't trigger a page refresh. Since React is used to create single page applications most of the time make sure you choose Link or Navlink when working with routing
The component allows you to do more than the normal link element. For instance, because it's a React component you have the benefits of having a state and what not (if you want that). You can see more documentation on here. Without the error I'm not sure what happens, but I suspect the routing library wants you to use the component, over a normal html element.
With regards to () => {} this is a construct which is called an anonymous function, or a lambda expression. It's basically the same as saving a function in a variable: var x = function(){ return (<div>...) }; if you have anything in the first parenthesis, it's a parameter which you have access to: const x = (y) => return y*2; The reason it's done in React is to expose the function scope to the component it lies in.
There is no better then looking at the code source.
https://github.com/ReactTraining/react-router/blob/master/packages/react-router-dom/modules/Link.js
You can see that Link is a component, that internally use history. Which is the module|library behind the history and navigation for react-router. And come with different modes (in memory history, browserHistory, hashHistory. And even custom).
Yea as a similarity it render an anchor tag but the default behavior is overridden (preventDefault()). They could have used just a div. But not completely right. As for the reason bellow.
So basically it work like that:
Observe the condition bellow
if (
!event.defaultPrevented && // onClick prevented default
event.button === 0 && // ignore everything but left clicks
(!this.props.target || this.props.target === "_self") && // let browser handle "target=_blank" etc.
!isModifiedEvent(event) // ignore clicks with modifier keys
) {
}
if the condition above is met. It will use history (push or replace). Otherwise it will leave the browser normal behavior. And so in that case it will be just a normal anchor tag <a />. Example letting the browser handle target='blank'. The condition are well explained.
Then depending on the type of history object. The behavior change. Not the behavior of ` itself. But just the result of the history object type.
In resume:
<Link /> is a component, that render a <a /> anchor tag. However in the main conditions the default behavior is prevented (preventDefault()). That allow it to apply the change to the history object (onClick event). Which react-router navigation is based on. And on the some conditions as mentioned above. It just fall back to the browser behavior. And just be exactly a <a /> anchor tag (no preventDefault()).
For the use. If you are using React-router. Then you just need to use Link.

switching between certain component on a page using nested route in react router

I am trying to create a job site. Following pages shows list of all the jobs which is shown once user hits search button from home page. So basically this is the second page.
In this page i am catching all the search parameter from url and fetching data from api and result is shown as below:
Once the user clicks individual joblist, detail page should load on the same page without changing header and fixed component with unique URL for the detail page. Expected result shown below:
My Problem:
I manage to create a nested Route, which renders detail page on the same page and also has a unique url. But it renders on top of existing job list. I mean if user clicks on joblist1, detail page renders on top of subsiquent list(above list: 2, 3, 4). But expected result is to only render detail page but not list of jobs when individual job list is clicked.
My code: I have only shown part of the code for brevity and simplicity.
1) jobs.js: Passes state data to child component to show list.
return(
<div>
<div>
fixed component
</div>
<div>
<RouteHandler />
<JobLists joblists={this.state.joblists} />
</div>
</div>
)
2) jobList.js: uses .map function to go through all data and handleclick function generate url and opens that url once user clicks individual link. Router catches nested route and loads value inside jobs.js in " ".
handleClick: function(i){
var base_path = window.location.protocol + '//' + window.location.host;
base_path += '/#/jobs-detail';
window.location= base_path;
},
render: function(){
var jobListRow = this.props.joblists.map(function(jobrowobj, i){
return(
<div key={jobrowobj.id} onClick={this.handleClick.bind(this, i)}>
<img src={jobrowobj.logo} alt="" />
<h3>{jobrowobj.title}</h3>
</div>
)
}.bind(this));
return(
<ul id="joblists">
{jobListRow}
</ul>
)
}
3) Route file:
var routes = (
<Route handler={App}>
<DefaultRoute handler={Home} />
<Route name="jobs" path="jobs" handler={Jobs}>
<Route name="jobs-detail" handler={JobDetail} />
</Route>
<NotFoundRoute handler={NotFoundPage} />
</Route>
);
I am not sure what is the best way to switch certain section (component) on a page as in my case switching between joblist component and jobdetail component. As you can see i am only able to load other component on top of existing component which is not the expected result.
Also would appreciate if any hint is given to maintain scroll position on the job list on user hitting back button.
I suggest you to upgrade your react-router to 1.0.0-rc1, and the API is more clear. Your problem is similar to the official introduction. The nested component will be passed as this.props.children, and you can insert it into the jobListRow.
About the scroll position, there's a github issue discussing how to restore it :)

Resources