Short version: When the user clicks a button, I want to do something, then reroute the user. How do I do this?
Longer version:
I have nested routes (don't know if this has any effect to the problem at hand) how this is setup is described in a pretty minimal (to the best of my knowledge) example here: link to SO question
I want a button, that first does something, then reroutes the user, therefore I can't use Link. First all I saw was
use this.props.history.push('/route') for that
But then I learned that this.props.history isn't a thing anymore and is now it's own npm package. Here I got a simple explenation on how to implement what I needed.
Example of my problem:
App.js render this:
<Router history={history}>
<div>
<Link to="/">
<button>Go to home</button>
</Link>
<Switch>
<Route exact path="/other" component={() => <Other/>} />
<Route path="/" component={() => <Home/>} />
</Switch>
</div>
</Router>
With two top level routes, an exact path for /other that render Other and looks like this:
and a relative path for / that render Home and looks like this:
Home renders this code:
<div>
THIS IS HOME WOO!
<div>
<Route exact path="/" component={() => <HomeController/>} />
<Route exact path="/about" component={() => <About/>} />
<Route exact path="/click-page" component={() => <ClickPage/>} />
</div>
</div>
Now here the default is HomeController (containing two buttons with links to about and click-page), but /about renders About and looks like this:
and /click-page renders ClickPage and looks like this:
This is where the problem starts :(
So ClickPage renders this:
<button onClick={clickHandler}>
DO SOMETHING THEN GO TO ABOUT
</button>
where clickHandler looks like this:
function clickHandler(){
console.log("doing stuff");
history.push('/about');
}
history in this case is imported from another file that looks like this:
// history.js
import { createBrowserHistory } from 'history'
export default createBrowserHistory({
/* pass a configuration object here if needed */
})
which is character by character copied from the answer i mentioned earlier.
Problem is, when I click the button, this is what I see:
So the function is called, it does something, and then it pushes "/about" to history, but nothing happens. Why is this? Do I need to force Home to rerender?
The full example code I've used to recreate the issue is here: pastebin-link
First thing is that,
this.props.history.push('/route')
is still present in the react-router4. the history prop is only available to the direct child of Router.
In case ur component is not direct child of the Router, u can export that component using the withRouter HOC, which can be imported like this
import {withRouter} from 'react-router-dom'
then, on click of button, u can do ur stuff, and use
this.props.history.push('/route')
to go to any location
You can use this.history.push in any component rendered by a Route.
Example code here: https://codesandbox.io/s/w21m5mlvyw
You can do it like this ..
import { Redirect } from 'react-router'
...
render() {
...
{fireRedirect && (
<Redirect to={from || '/thank-you'}/>
)}
Update your fireRediect state after your user action is completed
This looks like something you need here.
Related
I have a question about the React Router.
Now, I have a webpage with that looks like this:
As you can see there is another link on the page called "Custom Hooks".If I click on it, it will be shown something like this:
What I want now is to go to the a new page which the link shown in the image above, where the page will only shown me the "Example" and not the "This is UseState function" text.
In my code:
With my code, it cannot performs the route that I want to.
How can I fix this?
Thank you
This is happening because you seem to have misunderstood how the Switch component works. Think of it as a way of selecting what component to render based on the url. In the above code snippet,
<Link to="/Homepage"><Button>Back to Homepage</Button></Link>
<h1>This is UseState Function</h1>
<div>Example</div><Link to="/UseStatePage/UseStateFunction">Custom Hooks</Link>
is not under , meaning that whenever UseStateWebpage component is rendered, the Link, h1 and div with 'This is UseState Function' will always be rendered. If you want it to be rendered optionally based on the URL, there are multiple ways to achieve it. One way is as follows.
<Router>
<Switch>
<Route path="/UseStatePage" exact render={
()=>{
return(
<React.Fragment>
<Link to="/Homepage"><Button>Back to Homepage</Button></Link>
<h1>This is UseState Function</h1>
<div>Example</div><Link to="/UseStatePage/UseStateFunction">Custom Hooks</Link>
<React.Fragment>
)
}
}/>
</Switch>
<Switch>
<Route path="/UseStatePage/UseStateFunction" exact component={UseStateFunction}/>
<Switch>
<Router>
Neither the h1 nor div with example text are rendered into a route so they will always be rendered. Render the "homepage" content into its own route, something like the following.
function useStateWebPage() {
return (
<Router>
<Link to="/Homepage">Back To Homepage</Link>
<Switch>
<Route
path="/UseStatePage/UseStateFunction"
component={UseStateFunction}
/>
<Route path="/Homepage>
<h1>This is UseState function</h1>
<div>
Example
<Link to="/UseStatePage/UseStateFunction">custom Hooks</Link>
</div>
</Route>
</Switch>
</Router>
);
}
So basically, I have a problem with react router not rendering my SystemSidebar. I want to scroll through my SystemSidebar components, but my problem is when I press on 'LinkSoundIcon' it redirects me to 'a new page' but that page doesnt render my systemSidebar . I want when I press on any of the links of my sidebar that my sidebar remains
import React from 'react'
import './SystemSidebar.css'
import SoundIcon from '#material-ui/icons/Computer';
import ComputerIcon from '#material-ui/icons/Computer';
import { BrowserRouter, Route, Switch, Link } from 'react-router-dom';
import Sound from './Sound';
import Computer from './Computer;
const SystemSidebar=()=> {
return (
<div className='system'>
<div className="sidebar">
<Link to='Sound'><VolumeUpIcon /></Link>
<h4> Sound</h4>
<Link to='Computer'><ComputerIcon /></Link>
<h4> Computer</h4>
</div>
</div>
);
};
import React,{Component} from 'react'
import Sound from './Sound';
import Computer from './Computer';
import SystemSidebar from './SystemSidebar';
class MainSystem extends Component {
render(){
return (
<div className="MAIN">
<BrowserRouter>
<SystemSidebar />
<Switch>
<Route exact path="/" component={SystemSidebar} />
<Route exact path="/Sound" component={Sound}/>
<Route exact path="/Computer" component={Computer}/>
</Switch>
</BrowserRouter>
</div>
);
}
}
export default MainSystem;
<Link to='/Sound'><VolumeUpIcon /></Link>
answer of your first problem and second if you want to access sidebar in each component then don't put it in switch route , simply put it outside the routing... or if u want to access it with specific route then try using nested routing
Okay, so it seems a little wonky with your copy pasting (I hope this is just a problem that stems from copy and pasting and it's not like that in your code). But your Problem is here:
<Route exact path="/Sound" component={Sound}/>
You're saying here that the route should be EXACTLY http://<your root uri>/Sound
You should also use this exact route in the link if you want to hit it, this means you need to have the slash there:
<Link to='/Sound'><VolumeUpIcon /></Link>
Update:
So according to your comment you want the sidebar to stay when you click a link. In this case, take a look at your code:
<Switch>
<Route exact path="/" component={SystemSidebar} />
<Route exact path="/Sound" component={Sound}/>
<Route exact path="/Computer" component={Computer}/>
</Switch>
You define here that the component SystemSidebar will only be loaded when you're at the Root directory ("/") of your App. It will be unloaded when you change that directory, for example, to "/Sound". SystemSidebar will be unloaded and Sound will be loaded instead.
Since your Sidebar should always be shown, it needs to be in your Main App and outside of your actual Router logic. Remember what the React Router does: It switches out components depending on which directory (which Sub-URL) you're in. It's best practice to have a Sidebar, an App Bar or similar things that are always there to be their own components. Your actual content should live in a separate container, where the needed component can be swapped out by the Router if need be. So something like this:
class MainSystem extends Component {
render(){
return (
<div className="MAIN">
<SystemSidebar />
<div className="ContentContainer">
<BrowserRouter>
<Switch>
<Route exact path="/Sound" component={Sound}/>
<Route exact path="/Computer" component={Computer}/>
{/* Route "/" should be last because it acts as fallback! */}
<Route exact path="/" component={StartPage} />
</Switch>
</BrowserRouter>
</div>
</div>
);
}
}
That's pretty basic but I hope you get the gist of it.
Also, I'd encourage you to take a look at an UI framework like Material UI for example. It already provides components ready for use (like your Sidebar which is called Drawer there), it's mobile first and easy to use with responsive design.
I have a table which is as shown below
I have a component which displays the details
How to display the details when click on the first tab of the table
i.e, when clicked on the number 1053 in the first column how to redirect to component with the parameter
Not sure how to build the route localhost:3000/#/sitepage/1053
Is there a way to directly get the route localhost:3000/#/sitepage/1053 so that i can give in the href of the table
Read about https://www.tutorialspoint.com/reactjs/reactjs_router.htm
but not sure how this link to route to write
Please point me to a document which will help me in doing
It you want to redirect to another component, you can use React Router Redirect
This section of React Router documentation will help you, since you haven't posted any code you tried I assumed you're looking for just some documentation to guide you.
Update:
Ok let's assume you have a Router like that:
<Router>
<Route exact path="/" render={(props) => <Table {...props} />}/>
<Route exact path="/column-details/:id" render={(props) => <ColumnDetails {...props} />}/>
</Router>
The props here has a history attribute, so in your <Table> you can have access to it by initializing your component like this
function Table({history}) {
// ...
}
Now in the first column, you need to add an onClick listener, let's say you use <TableCell> that would be
<TableCell onClick={() => history.push(`/column-details/${yourId}`)}/>
Now in your <ColumnDetails> component, we will need access to match attribute to extract our id from the url, so it would be something like:
function ColumnDetails({match}) {
const id = match.params.id;
// ...
}
I hope this is clear enough.
I think you can try react-router-dom to solve your problem, react-router-dom is a wrapper on top of react-router, which brings more flexibility
Let say your table is under Equipments component, then you have to add <Link/> component as alternate to <a> as follows, assuming your equipment object got url and title property
<Link to={`${equimpement.url}`}>{equimpement.title}</Link>
Please note that if you want to use Link component you have to import it on top
import { Link } from "react-router-dom";
And you can manage your routing in your App.js like this way
<div className="content">
<Switch>
<Route path="/not-found" component={NotFount} />
<Route path="/equipments/:id" exact component={EquipmentForm} />
<Route path="/equipments" component={Equipments} />
<Route path="/" exact component={Home} />
<Redirect to="/not-found" />
</Switch>
</div>
Make sure you have imported Route, Redirect and Switch as well in your on top of your App.js
import { Route, Redirect, Switch } from "react-router-dom";
Update
And last if you want to use Route in your app you must wrap your with BrwoserRouter
ReactDOM.render(
<BrowserRouter>
<App />
</BrowserRouter>,
document.getElementById("root")
);
with its import import { BrowserRouter } from "react-router-dom";
I'm working on an user-list project made with React/ React Router for a community I'm part of and I've been running into a problem I can not find the root of.
I have a nested Router set up, to have a userlist appear on the click of a button (url/player) and then - in the userlist - have the profile of that user appear on a click on the name (url/player/:id). This works fine so far!
BUT:
When I'm on a user profile (url/player/:id) and click the link to get back to the userlist, it does not render the userlist-component - though the url in the browser itself changes back to (url/player).
I can't figure out how to make the general userlist reappear again and would surely appreciate some input.
Since the project has multiple components, I separated them into different files, where my problem my lay.
I still tried to reconstruct the instructions of different tutorials for nested Routes. Maybe I'm just overlooking something basic, but I cant seem to find it.
Main Navigation Router in the index.js
<Router>
<Navigation />
<div className="contentBox">
<Route path="/" exact component={Home} />
<Route path="/player" exact component={Playerlist} />
</div>
</Router>;
Userlist Router in the Playerlist-Component
<Router>
<Route path="/player" exact component={Playerlist} />
<Route path="/player/:id" component={Playerprofile} />
</Router>;
The weird thing is, only the Playerlist-Link does not work anylonger. If I click on either the "Home"-Link or any other Navigation, it works. So I assume it has something to do with the rerendering.
I'm sorry if this question seems obvious, I'm still a beginner and appreciate any help! Thank you!
You should improve the nested routing of your app:
No need to wrap PlayerList component with Router. Use Router only in root component.
Don't use Route to PlayerList component within the component itself.
Do use Route to PlayerList component in index.js, but without exact (so routing to '/player/:id' routes work).
Here are snippets of updated code:
App.js (in your case index.js):
export default function App() {
return (
<Router>
<Navigation />
<Route path="/" exact component={ Home } />
<Route path="/player" component={ PlayerList } />
</Router>
);
}
PlayerList.js
export default function PlayerList({ match }) {
return (
<div>
<h1>Player List</h1>
<Route path={`${match.path}/:id`} component={ PlayerProfile } />
</div>
);
}
PlayerProfile.js
export default function PlayerProfile({ match }) {
return <div>Player Profile { match.params.id }</div>;
}
Navigation.js (draft):
export default function Navigation() {
return <nav>
<Link to="/">Home</Link>
<Link to="/player">PlayerList</Link>
<Link to="/player/1">Player 1</Link>
<Link to="/player/2">Player 2</Link>
<Link to="/player/3">Player 3</Link>
</nav>;
}
Though I'd recommend to move the Links to "/player/:id" into PlayerList component.
You can find official notes on nested routing here. Hope that helps.
I am using the following simple nav code
<Router>
<Switch>
<Route exact path='/dashboard' component={Dashboard} />
<Route path='/dashboard/accounts' component={AccountPage} />
</Switch>
</Router>
<NavLink exact to={'/dashboard'}
disabled={this.props.item.disabled}
activeClassName='active'>
<NavLink exact to={'/dashboard/accounts'}
disabled={this.props.item.disabled}
activeClassName='active'>
The URL changes but the view does not. It does however change when I refresh the page or manually go to that URL.
You can also use the:
import { withRouter } from 'react-router-dom';
And then on your export default, you do like this:
export default withRouter(connect(mapStateToProps, {})(Layout));
Because when you have an export connect, you need to tell that that component will be using the router.
This is because react-redux connect method implements shouldComponentUpdate which will cause component not to render when props didn't change. And this is conflicting now with react-router 4.
To avoid it you can pass {pure: false} to connect as described in react-redux troubleshooting section.
Another way is to use withRouter HOC or pass location prop like described in DOCS.
I had my Navlinks in a stateless-component (or dumb component) and a container to control the collapse-state of my navbar.
after switching the navbar-container from PureComponent to Componentit solved the problem for me.
I have encountered this problem. I resolve it by add attribute key to component Switch with value is a location pathname and location search.
Have you tried making sure that your router tags wrap the entire chunk of code?
<Router>
<Switch>
<Route exact path='/dashboard' component={Dashboard} />
<Route path='/dashboard/accounts' component={AccountPage} />
</Switch>
<NavLink exact to={'/dashboard'}
disabled={this.props.item.disabled}
activeClassName='active'>
<NavLink exact to={'/dashboard/accounts'}
disabled={this.props.item.disabled}
activeClassName='active'>
</Router>
It looks odd, but including links into the <Router> propagates your path change to router components when you click the link and actually renders the component you are routing to. Just fixed a very similar problem myself.