React Router - Change state of other components - javascript

When using React Router, how can I change the state of another component when the page is being routed?
For example, I have an app structured like this:
Header
==Link1 Link2==
Footer: "You are now at Home", button1, button2
When the user is routed to Link1, I would like to update the state of the footer so that it knows that it is currently on link1. For example, the footer is a toolbar, and some buttons will be disabled on Link1, but all buttons will be available on both Link2 and Home. Like this:
==Header==
==Link1 Link2==
==Content of Link1==
Footer: "You are now at Link1", button2
The solution I can think of is to wrap both the footer and content of the links into a big, single component, and set up routes to that whole component. But I'm not sure if this is a good practice, since I guess it kind of defeats the purpose of a "footer".
Another thought is that maybe I can change the state of the footer component when routing is in action, so the footer will re-render when it's necessary. The <Footer /> component thus sits outside of the main contents, on the same level like the <Header />. However, I have no idea how to do this.
Is there a better way to achieve this?
Here is the code snippet on CodeSandbox, I hope it could illustrate my problem better.
CodeSandbox demo

One way to go would be to use the withRouter HOC on the Footer so that it receives the props when the route is changed.
Changes to code
import { BrowserRouter, Route, Link, withRouter } from "react-router-dom";
then
<BrowserRouter>
<div>
<Route path="/" exact component={Home} />
<Route path="/section1" component={SectionOne} />
<WrappedFooter />
</div>
</BrowserRouter>
and then
const WrappedFooter = withRouter(Footer);
You can now use the this.props.location.pathname inside the footer to access the currently matched route from the router. You should use the actual path in your switch
let text,
{location} = this.props
switch (location.pathname) {
case '/': {
text = 'Home'
break
}
case '/section1': {
text = 'Section One'
break
}
}
https://codesandbox.io/s/8y051k9qoj

Related

Updating other React components

I have a Navbar that has buttons that change the language of the website. Instead of using different htmls, I'm just replacing the text with what I have in a database. However, doing anything in a component only updates that component and I need a way to update every other one
I haven't tried anything useful yet, since there's a complicated layout for usual solutions.
App.js
function App() {
return (
<div className="App">
<Router>
<div className="container" id="main">
<Navbar />
<Switch>
<Route exact path="/" component={Home}></Route>
<Route path="/gallery" component={Gallery}></Route>
</Switch>
</div>
</Router>
</div>
);
}
export default App;
My Navbar is outside of Switch that contains all the different pages I need to update.
All I need to do is to somehow pass an update request to every other component. Would be nice if it'd happen without reloading the page, though that would somewhat work.
You don't mention using Redux, so I assuming you are just using React state. Keep the language in the state of the top-level component, and pass the language to the components that need it. Give Switch a call-back function that changes the top-level state when the language is changed.

Cache react-router route

I have the following code:
<View>
<Header />
<List />
</View>
If the user clicks on an edit button in a list item, react-router changes the page from /list to /list/{itemId}. This is a new page. After the user filled in some information and clicks on save, react-router changes back to /list. Now the full /list-page is rerendered!
Is there a way to cache a page, so react can detect the changes and rerender only them instead of the full page again?
It should also be possible to render the page directly (not over /list -> /list/{itemId}) so no modal solution I think.
I'm using react-router-redux v5.0.0-alpha.9 so the solution should be compatible with it. It should also be compatible with react-native and react-dom.
If you are working with react-router
Component can not be cached while going forward or back which lead to losing data and interaction while using Route
Component would be unmounted when Route was unmatched
After reading source code of Route we found that using children prop as a function could help to control rendering behavior.
Hiding instead of Removing would fix this issue.
I am already fixed it with my tools react-router-cache-route
Usage
Replace <Route> with <CacheRoute>
Replace <Switch> with <CacheSwitch>
If you want real <KeepAlive /> for React
I have my implementation react-activation
Online Demo
Usage
import KeepAlive, { AliveScope } from 'react-activation'
function App() {
const [show, setShow] = useState(true)
return (
<AliveScope>
<button onClick={() => setShow(show => !show)}>Toggle</button>
{show && (
<KeepAlive>
<Test />
</KeepAlive>
)}
</AliveScope>
)
}
The implementation principle is easy to say.
Because React will unload components that are in the intrinsic component hierarchy, we need to extract the components in <KeepAlive>, that is, their children props, and render them into a component that will not be unloaded.
Got into same problem recently and my initial nudge was to use something like react-router-cache-route or another package.
But main disadvantage of third-party packages is that they are usually slow to support new react-router versions and source code seemed to me overly complex for this problem.
So I researched further and stumbled on this suggestion from Dan Abramov and ended up with solution like this:
function App() {
const itemId = useParams<{ itemId: string }>();
return (
<Switch>
...
<Route
exact
path={['some-list-route', 'some-list-route/:itemId']}
>
<List style={itemId ? { display: 'none' } : undefined} />
{itemId && <ListItem />}
</Route>
...
</Switch>
)
}
We keep both list and item routes under single Route component to have control over their mounting behaviour. When we get item route — we hide our <List /> (but it stays mounted, "kept-alive") and conditionally mount <ListItem />.
We can also pass itemId to <List /> as a prop, if we need more control, like conditionally trigger data fetching only for list route.

React Router v4 NavLink active route

I'm trying to port my project from using v3 of react-router to v4 of what is now called react-router-dom. Now the problems arise when I have a MenuBar component, which is completely separate from the routing logic (as you'd expect), because it will show the exact same links whatever the current path may be. Now that worked all nicely with v3, but now when I'm using NavLink, which has the same activeClassName property, the active route does not update on the NavBar, only on refresh. That seems a bit dumb, so there must be a way around this.
export default #inject('ui') #observer class App extends Component {
render() {
return (
<Router>
<div className={ styles.wrapper }>
<Sidebar />
<main className={ `${styles.main} ${!this.props.ui.menuOpen && styles.closed}` }>
<Route exact path="/" component={ HomePage } />
<Route path="/signup" component={ SignUpPage } />
<Route path="/login" component={ LoginPage } />
<Route path="/about" component={ AboutPage } />
</main>
<footer className="site-footer"></footer>
</div>
</Router>
);
}
}
The above is my main App logic and as you can see the routes are nested, but the Router itself wraps around the whole component.
What should I add to make them work again? (They do work properly on page refresh)
Based on your usage of the #observer decorator, it appears that you are using mobx-react. The thing to understand about observer is that it implements shouldComponentUpdate to optimize rendering performance. That sCU call will look at the current and next props, and if there is no difference, it will not re-render. This is a problem because by default, React Router uses the context to pass data and relies on elements re-rendering to get the updated values, but observer's sCU has no way to detect context changes.
The way that you can get around this is by passing the location object as a prop to the component that is wrapped by observer. Then, when the location changes, observer's shouldComponentUpdate will detect the difference and re-render.
You can see the blocked updates guide for more information.

react-router link to element by id within another component

I'm using React Router and I'm trying to link to a sub-component in another route by id. Basically what would usually be done using the <a href="www.url.com/profile/#profile-header-id">.
I'm not sure if there's a built in way for react router to do this, but if not perhaps I can manually trigger the link at a later point when I know the element has been rendered.
The issue isn't linking to another route which of course is done with the Link from react router. The issue is linking to an element which is found in the rendered HTML of the linked component.
Less Abstract Code Example:
So let's say my router is
<Route path"/A" component={A}>
<Route path"/B" component={B}>
component A has the following render:
render(){
<div>
// A looooot of text and other HTML elements
<div id="relevant-to-B">
// relevant stuff for component B
</div>
</div>
}
Now in component B, I want a Link that not only takes me to the Route "/A", but also scrolls to the element of id #relevant-to-B, thereby skipping all the irrelevant stuff.

React-Router is refreshing page when I follow a route using <a> tag

I'm building a React app that has links pointing to predefined routes.
Click Here
The routes resolve fine, but it's refreshing the page, thereby slowing down app performance. How do I avoid re-rendering the entire page?
Fix the problem by using the <Link> tag included with react-router.
import React from "react";
import { Link } from 'react-router-dom';
export class ToolTip extends React.Component {
render() {
return (
<Link to="/My/Route">Click Here</Link>
)
}
};
First answer was correct but I didn't found Link from react-router-dom. It was in my case here:
import { Link } from 'react-router';
You need to:
import { Link } from "react-router-dom"
then import the component you wish to go to
import Example from "./component/Example"
Then use Link like this
<Link to="/Example">
<h4>Example Page</h4>
</Link>
This will stop the refreshing.
Note that, if to="/Example" matches a route you've specified in your BrowserRouter and then it sends you there.
Learn more here Reat Training / React Router
Hi semantic ui react example
<Menu.Item name="NotFound" as={NavLink} to="/dadsadsa" />
In case the above methods don't work, check if you are importing the right component where you are defining the routes. (In my case, I imported a component with the same name but from a wrong path)

Categories

Resources