Update property of React component in a separate component - reactjs

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.

Related

React Rerender with radioButtons using redux

I have a question, I will appreciate if you could help me. I was building a simple react movie app, using redux for manage the state. I also use react router dom for manage basic routes.
I have the home page, where I render some radio buttons that when clicked they dispatch an action to filter the movies by a genre.
So my question is that when I clicked in a radio button it made the filter but the radio button it not checked.
Whereas if I put the Home component outside the Route it do it well, the radio button is checked and it filter the movies list.
This is the app component, with the home outside the router working well and the home inside the routing.
<Home props={props}/>
<Switch>
<Route exact path='/home' component={() => <Home props={props}/>} />
<Route path='/genre=:genre' component={detailGenre} />
<Redirect to='/home' />
</Switch>
This is the radio button
const handleRadioButtons = (e) => {
sortMovies(e.target.value)
}
...
<label htmlFor='horror'>Horror</label>
<input onChange={handleRadioButtons} name='filmtype' value='horror' type='radio' id='horror' />
I'm sure i'm not understanding some behaviour or maybe i'm missing something.
Thanks in advance.
Why is this happening and how can I solve it ?
The component prop tells React Router to generate the component using React.createElement, and if you pass it an inline function it will execute that function and generate the component anew with every single render. In other words, your Home component is being unmounted and remounted with every render. Try this instead, which is the standard way of rendering a component via a route with props:
<Route exact path='/home' render={() => (<Home props={props}/>)} />

Cannot call the component in route

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>

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 Route doesn't pick up the url change

I have something like this:
<Route path="/route/:param" component={DealContainer} />
Then while the component is mounted I am doing a client side redirect:
componentWillMount() {
if (this.props.match.params.param != 'desired_one') {
this.props.history.push('/route/desired_one');
Despite the fact that the url changes the component is not remounted...
Any ideas?
You should resolve this issue by using the Redirect component inside "react-router-dom" package
<Route exact path="/route" component={DealContainer} />
<Route
exact
path="/route/desired"
render={() => <Redirect to="/route/desiredRedirectLocation" />}
/>
<Route path="/route/:param" component={DealContainer} />
This implementation should:
Match the exact base route correctly, without matching any props. Lets say that you want to show a list of items (e.g. /products)
Match the desired item to be redirected (e.g. products/car) and redirect it to the desired location, let's say products/horse
Match any other case that you don't want to redirect /products/:id and correctly take the prop you are after inside the match object.
Explanation
The problem with history.push, is that React will figure out you are using the same component, and will only update the differences, without actually re-mounting the component, which is already mounted.
My example with redirect on the other hand, will intercept the route you want to redirect without mounting the component first, so component will be mounted after the redirect happened, correctly executing the action that you need.

React-router-dom and Redirect not being added to history?

Outline:
I'm currently trying to learn react/redux/router. As a practice project, I'm working on a basic contact/client management app. While using react-router-doms Redirect component. I can't get the back/forward navigation to work as expected.
Details:
I have a table with a list of entries located at /contacts/ and when you click on one of the table rows, I want to Redirect using a value set by the <tr>s onClick attribute.
The click on <tr key={d._id} onClick={()=>this.setState({ id: d._id })}> changes state.id to the clientIDs unique value. This results in a Redirect being true in the tables render method, triggering the Redirect.
render() { // render method of '/contacts/' {Contacts} component
return(
... // table head
{ this.state.id && <Redirect to={"/contacts/card/"+this.state.id}/> }
...// table content
)
}
This is the router which is located in the parent App.js
render() {
return (
<BrowserRouter basename="/" >
<div>
<NavBar/>
<main>
<Switch>
<Route exact path="/" component={Login}/>
<Route
exact path="/contacts" component={Contacts}/>
<Route
exact
path="/contacts/card/:clientID" component={ContactCard}/>
</Switch>
</main>
</div>
</BrowserRouter>
);
}
Question:
After the redirect, if you click the back button from /contacts/card/:clientID, the browser doesn't go back to /contacts/. instead, it cycles through any previous :clientID URLs that I've looked at.
I have tried wrapping the <tr> in a router <Link to={}> tag, but it destroyed styles.
Caveat: (this.state.id)
I know that I should be using Redux to hold the value of this.state.id for my redirect function. I haven't fully grasped how to mange multiple values with a single redux reducer yet. I will update question with the appropriate method once I do.
Found the answer, I needed to add push to my <Redirect/> component like so.
<Redirect push to={`/contacts/card/${this.state.id}`}/>
From the docs:
<Redirect/>
push: bool
When true, redirecting will push a new entry onto the history instead of replacing the current one.

Resources