Cannot call the component in route - reactjs

Basically, I am trying to pass an id value and render the associated data. however, when I want to call the component to render data my route does not call the component.
I have 2 components. (Main and Teams)
Main Component
render() {
const OneTeam = ({match}) => {
console.log("Never logs this!");
return (
/*Let's assume I am returning simple HTML here */
<p>Hello</p>
);
};
return (
<Switch>
<Route
path="/"
component={() => <Teams teams={this.props.teams} />}
/>
<Route path="/:teamId" component={OneTeam} />
</Switch>
);
}
It never goes into OneTeam component.
Teams Component
<Link to={`/${team.id}`}>Details</Link>
I can pass the id value correctly.
What happens?
As you can see Teams component is my homepage. So here when I click a Team (there is no Team component, you can think of a card), I get the id and with <Link> I can push my id to URL (localhost:5001/98), however, nothing else happens. In the Main component,<Route path ="/:teamId" component={OneTeam} seems don't work at all because I cannot render OneTeam component, even I am not able to console.log in that component.
I am not sure I am missing something because I just try to do a very basic thing. By the way, I use Redux if it is helpful to figure out. Why I cannot go into OneTeam component or function.

You should swap the order in which the routes are called. It will render the first route it matches, which is your home and your detail view will never get evaluated. But if you switch the order, the detail view will get evaluated before that and if it matches, it will be rendered.

It is unusual to use a parameter on /, because it makes it difficult to extend your application to other pages and unnecessarily complex to distinguish whether any other route is a team id or a different page. You should do something like:
<Route
exact
path="/"
component={() => <Teams teams={this.props.teams} />}
/>
<Route exact path="/team/:teamId" component={OneTeam} />
and then
<Link to={`/team/${team.id}`}>Details</Link>

Related

React router: Redirecting with url param?

In my render function I have
<Route path="/classes/:course" render={(match) => (
<Redirect to={`/classes/${match.params.course}/home`} />
)}>
<Route path="/home" component={Home} />
</Route>
For example if the param for "course" was "BIO1001", I want to redirect the page to "/classes/BIO1001/home" when I go to the page "/classes/BIO1001/". Previously I tried simply putting a Redirect tag with "from" and "to" but ran into the problem of the url actually going to "/classes/:course/home" instead of "/classes/BIO1001/home"
Also would the nested route with path="/home" go to "/classes/BIO1001/home"? I was unsure on how I can set a route where the path starts from the previous url (in this case starting from "/classes/:course/"
The first problem is right here:
render={(match) => ( ...
The render function gets a props object which contains a match property. Instead of destructuring the match property, what you are actually doing is assigning the whole props object to a variable match. So when you go to access match.params it won't be found.
You need curly braces around match in order to destructure it.
render={({match}) => ( ...
The second problem is the nesting of the two Route components. I get a warning:
Warning: You should not use <Route render> and <Route children> in the same route; <Route render> will be ignored
So based on that warning you can see that your Redirect is being entirely ignored since it comes from render. The child Route is seen as the render function for the classes Route.
I'm assuming you have various subpages of a course? And we want to force the URL to include "/home" if none is set? (Personally I would do the opposite and redirect "/home" to the course root URL).
Previously I tried simply putting a Redirect tag with "from" and "to" but ran into the problem of the url actually going to "/classes/:course/home" instead of "/classes/BIO1001/home"
Per the docs, you can use params in your Redirect, but only if it is inside a Switch.
Here's a sample code to do that:
const CoursePage = () => {
// you can access arguments from the props or through hooks
const { course, tab } = useParams();
// not sure how you want to handle the different tabs
return <div>Viewing Course {course}</div>;
};
const App = () => (
<BrowserRouter>
<Switch>
<Route path="/classes/:course/:tab"><CoursePage/></Route>
<Redirect from="/classes/:course" to="/classes/:course/home"/>
</Switch>
</BrowserRouter>
);
export default App;
Your nested routing is true i think. But you are rendering your Home component without any dynamic props. Try it like below:
<Route path="/classes/:course" render={(match) => (
<Redirect to={`/classes/${match.params.course}/home`} />
)}>
<Route path="/home" >
<Home someProps={someValue} />
</Route>
</Route>
Note: The Redirect element can be used without a containing Route element by providing a from property. In this case, you can just use the URL parameter tokens in both from and to, and they'll be carried over for you. For example, if you're using a Switch block...
<Switch>
{/* various app routes... */}
{/* redirect action */}
<Redirect from="/classes/:course" to="/classes/:course/home" />
<Switch>

Hide React Elements Based on Session Storage or Page

I have React application which has a structure similar to the following.
class App extends Component {
render() {
return (
<div className="App">
<NavBar />
<Router>
<Switch>
<Route path="/login" component={LoginPage} />
<Route path="/" exact component={DashboardPage} />
<Route path="/admin" exact component={AdminPage} />
// many other routes
<Route component={NotFound} />
</Switch>
</Router>
</div>
);
}
}
I do not want the login page to display the <NavBar /> element. I tried using the sessionStorage to get the userId and only display the navigation if the value is set. When I do this and go to the login page and the nav bar is not there. But when I log in, it's still not there. If I refresh however, it will appear.
I know one way to solve this is to make some sort of wrapper around the pages that do want the navigation, but I'd rather not have all of that code duplication, etc.
I feel this must be a common want, and I'm missing something dumb. Any help would be appreciated. I'm new to React so I don't follow everything that's going on here. Thanks.
I think your way of conditionally showing the NavBar is the right way. The question is how to trigger a state change so that the render method takes care of hiding and showing the NavBar, when you log in and out. I suggested maintaining a isLoggedIn state in your App component, and rendering the NavBar based on that, instead of directly accessing the SessionStorage. You could then use a custom event to update the state, when SessionStorage changes.
See this question for updating state based on Storage (in short, you fire and handle a custom event for storage changes): How to listen to localstorage in react.js
This might still be more code that you had hoped for, but it's more aligned with how React works, to derive the view (render) from component state.

Route + Render + Redirect with react-router-dom

I just took over a React project from one of my colleague but I cannot understand the logic in the code below.
content = <Switch>
<Route path="/login" exact component={LoginPage} />
<Route render={() => { return <Redirect to="/login" />; }} />
</Switch>
I know how to use Route with Component, with Render, but Render with Redirect, first time I saw it.
Thanks
This appears to be just another way of just saying:
<Redirect path='*' to='/login' />
Since it is inside a <Switch>, and after any <Route>, it will always match (if nothing above it got matched) and get rendered.
When the Redirect component gets rendered, it does its job of redirecting to the page specified in the to prop.
I found that out by doing some reading of the source code. If you're interested, there's a bit of indirection, but basically the Redirect component renders a Lifecycle component which will call method with the location provided as soon as it's mounted.
method is set like this:
const method = push ? history.push : history.replace;
And that's done like that because apparently the <Redirect> component can take push as a boolean prop, to set the behaviour of how the redirect is actually achieved.
Redirect component source https://github.com/ReactTraining/react-router/blob/master/packages/react-router/modules/Redirect.js
Lifecycle component source: https://github.com/ReactTraining/react-router/blob/master/packages/react-router/modules/Lifecycle.js

React - How to route parent div from child div

I'm attempting to route the main app from a selection in one of its children's children's components. My project is structured like this:
<Main>
<titlebar>
<Router>
<Link to={"/home}>Home</Link>
<Link to={"/search}>Search</Link>
...
</Router>
<Switch>
<Route exact path="/" component={Home}
<Route exact path="/search" component={Search} />
<Switch>
<footer>
</Main>
<Home>
<Messages and stuff>
...
<List />
...
<Home>
<List>
list.map => (
<button?>View</button>
<thing.id>
...
)
</List>
<View>
...info about thing ...
</View>
My goal is to be able to click on a specific button inside List and have Main reroute from site/ to site/view{id}, similar to the way clicking on "search" in Main would route to site/search. I don't want to have View be inside Home or List, I want it to be a child of Main. How would I go about this? Passing a function that manually switches? Linking something somewhere? I'm relatively new to React and routers, but I didn't find anything from when I looked through the docs.
There are two ways to do it
To pass a function from main to child as props to retrieve id of clicked and load view data accordingly
Hence you are using react router you could add one more route
Look at below example link for more details
[CodePen] https://codepen.io/dwarka/pen/PLEMWX
Welcome!
You would need a route that looks something like this...
<Route path="/view/:id" component={View} />
and a Link like
<Link to=/view/${thing.id}>Click me!</Link>
Note the :id which denotes that there is a variable after the view/
Then in your View component you'll access the ID via this.props.location
If you'd rather pass the entire list item and not just the ID (because the data isn't accessible from the View component for whatever reason) you can also send the entire item like so
<Link to={{ pathname: '/view', state: thing }}>Click Me!</Link>
Then in the view component look at this.props.location.state
Also just a heads up, you don't need exact in a route unless you plan to have things after the trailing slash.
For example...
route="/" Needs exact
route="/item" Does not need exact
route="/season" Needs exact cause it has subroutes (spring)
route="/season/spring" Does not need exact because it has no further subroutes

Update property of React component in a separate component

I have a React MaterialUI AppBarcomponent with property title , that I am changing based on the value returned by window.location.pathname. So as the page/url changes, the title will change with it. Looks something like below:
<AppBar
title={this.renderTitle()}
/>
renderTitle() {
if (window.location.pathname === '/home'
return 'home';
} else if (window.location.pathname === '/login'
return 'login';
}
The issue I am running into is that renderTitle() does not get executed if a different component (so not the AppBar) causes the page/url change.
E.g. another separate React component on the page triggers the page to change, which I'd hoped with trigger renderTitle(), but it doesn't... thus the title property never updates. So if I am navigating from /home to /login, the following will happen:
pathname is /home
user presses a button which runs a function, submit(), which is used to change the page w/ react-router
renderTitle() is run at this point, but window.location.pathname is still returning the previous page
submit() changes the page to /login
window.location.pathname is now correctly set to /login, but it is too late as renderTitle() has already been run
any help is appreciated, thanks
The best way is to use react-document-title library.
From documentation:
react-document-title provides a declarative way to specify document.title in a single-page app.
This component can be used on server side as well.
If your component that renders AppBar is an actual route you can just read the pathname from the props that react router injects.
<Router>
<Route path="/" component={App}>
<Route path="about" component={About} />
<Route path="inbox" component={Inbox}>
<Route path="messages/:id" component={Message} />
</Route>
</Route>
</Router>
For example in App, About, Inbox and Message you have access to the router props. And you can also pass the props to their children.
render() {
return (
<AppBar
title={this.renderTitle(this.props.location.pathname)}
/>
);
}
And in your function just use the parameter to return the correct result. Now because you are using props your component will update automatically when they change.

Resources