I'm working on a dynamic webpage using React. I'm using a Switch component from React Routers to change from the home page to various other pages, as needed by the user. I want the following routing to create a new account:
Home page >> login page >> Create account (1) > (2) > (3) >> back to login or home page
In words: The user should be able to navigate from the home page to a login page, from the login page to a create account page with three steps (each a unique component), and from the third step back to the home page.
My trouble lies in getting from the third create-account page back to the home or login page. I can create links that bring me from Page 1 > 2, 2 > 1, 2 > 3, or 3 > 2. But I can't make page three render a link that will bring me back to Home.
I initially had a switch statement in my Create Account page rendering three separate components. If I set the route home inside of this switch statement, the div would render Home inside of it, as expected. If I simply omitted the route, the page would fail to render when I clicked on the link, but clearly had the right path because it would load properly upon refresh.
I've also tried:
Moving the Router to circumscribe the entire component, rather than just the Switch component.
Moving the Route to home before anything else in the document - as expected this just rendered the home page above the create account page upon activating the route.
Using a Redirect inside and outside of the Switch statement.
A really nasty method of rendering inside the div the value of a "component" key from inside an object, and manually updating the values in that object when the links are clicked, rather than using anything even vaguely associated with React Routers. This worked better than it should have, but not perfectly.
**As kind of a side note I've been told (though I have no personal experience) that Vue has a 'stepper' that could be used to navigate through pages 1, 2, and 3, to avoid using a nested router at all. Is there such a thing in React or React-Routers?
The App:
function App() {
return (
<Router>
<Switch>
<Route exact path="/" component={Home} />
<Route path="/Login" component={Login} />
<Route path="/CreateAccount" component={CreateAccount} />
<Route path="/Account_Recovery" component={Recovery} />
<Route path="/RR_One" component={RR_One} />
</Switch>
</Router>
);
}
The Create Account Page as it exists now:
class CreateAccount extends React.Component {
render() {
return (
<Router>
<PageHeader pageTitle="Create a Free Account"/>
<div className="pageBody">
<div className="centerHalf">
<Route path="/CreateAccount/1" component={PageOne} />
<Route path="/CreateAccount/2" component={PageTwo} />
<Route path="/CreateAccount/3" component={PageThree} />
</div>
<p>Paragraph outside of the changing div.</p>
</div>
</Router>
)}};
An example component PageThree (the others are similar for now, just linking between each other):
class PageThree extends React.Component {
render() {
return (
<div>
<p>Page Three</p>
<Link to={{pathname: "/CreateAccount/2"}}>Go to page 2</Link> <br />
<Link to={{pathname: "/Login"}}>Complete</Link>
</div>
)}};
The solution was actually very simple: I had misunderstood the purpose of the Router component, and the nesting of these (not of the switch statement or the routes) was the real problem. By removing the inner Router component (in my case, the one in the CreateAccount component) the navigation was able to flow smoothly from page three back to an external page.
class CreateAccount extends React.Component {
render() {
return (
<div>
<PageHeader pageTitle="Create a Free Account"/>
<div className="pageBody">
<div className="centerHalf">
<Route path="/CreateAccount/1" component={PageOne} />
<Route path="/CreateAccount/2" component={PageTwo} />
<Route path="/CreateAccount/3" component={PageThree} />
</div>
<p>Paragraph outside of the changing div.</p>
</div>
</div>
)}};
Related
Expected: when clicking on the image from one component(PhotosList), it routes to another page with the specified url to view the other component(Image details)
Reality: what happens that when clicking on the image, both components are rendered on the same page but with the different url.
using import { BrowserRouter as Router, Switch, Route, Link } from "react-router-dom";
Here's the return of the functional component(PhotosList)
return (
<Router>
<div className="layout">
<Masonry gutter={"5 px"} columnsCount={3}>
{photosList.map((photo: any) => (
<Link to={"/details"}>
<img src={photo.download_url} />
</Link>
))}
</Masonry>
<button onClick={loadMore} className="btn-grad">
{isLoading ? "Loading..." : "Load More"}
</button>
<Switch>
<Route exact path={"/details"} component={ImageDetails}></Route>
</Switch>
</div>
</Router>
);
and the app file is
const App = () => {
return (
<div className="App">
<header>Album</header>
<PhotosList />
</div>
);
};
export default App;
return (
<div className="layout">
<Masonry gutter={"5 px"} columnsCount={3}>
{photosList.map((photo: any) => (
<Link to={"/details"}>
<img src={photo.download_url} />
</Link>
))}
</Masonry>
<button onClick={loadMore} className="btn-grad">
{isLoading ? "Loading..." : "Load More"}
</button>
</div>
);
and in the app file
const App = () => {
return (
<Router>
<div className="App">
<header>Album</header>
<PhotosList />
<Switch>
<Route exact path={"/photilist"} component={PhotosList}></Route>
<Route exact path={"/details"} component={ImageDetails}></Route>
</Switch>
</div>
</Router>
);
};
export default App;
From what I can gather, you have rendered the PhotoList component which then renders a list of photos each with a link to /details.
In the same component you set up the /details route.
Whenever you click the Link which redirects to /details, the PhotoList component will (should) unmount. Once this component is unmounted, so is the Route which sets up the component which should be rendered on /details route. So you are on /details but the router has no idea what component should be rendered because the following code:
<Switch>
<Route exact path={"/details"} component={ImageDetails}></Route>
</Switch>
no longer exists.
To avoid this, don't put Routes this far down in your application. Routes should always be close to the top level (children/grandchildren of Router). This makes projects way easier to manage. I do have projects with upwards of 100 routes and I do split up my routes into seperate files (for example each module will have a ModuleRoutes.js file in which I set up the routes for that module), then you can create a RootRouter components which renders these seperate JS files (Which are really React components) and you render the RootRouter inside of your <BrowserRouter> / <Router>.
You can abstract this even higher by creating an array of objects which hold details about each route (route name, path, component for the route, function / data you want to pass down to the component, access control (true/false) defined as a logical expression of your choosing - i.e. user.isLoggedIn === true). By employing this abstraction you minimize the room for error because errors and bugs are much easier to spot when they are in this kind of form (object) as opposed to JSx which takes up more lines of code, has messy indentation and is not very clean in general.
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.
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.
I have a simple App component with Links to a User index and a Cache index (for a geocaching app). My Links render fine, and when clicked they change the path in the address bar, but nothing changes in the DOM until I refresh the page, at which point the page looks the way it should. What's going on here, and what's the conventional way of dealing with it? I am using Redux as well, if that makes any difference. The following is all of the JSX returned by my App component:
<div>
<nav>
<Link to="/caches">Caches</Link>
<Link to="/users">Users</Link>
</nav>
<div>
<Switch>
<Route exact path="/" component={Home}/>
<Route path="/users" render={() => <div><UserList users={this.props.users}/></div>}/>
<Route path="/caches" component={CacheList}/>
</Switch>
</div>
</div>
Its a common issue with react-router-4.
use {pure: false} in react-redux connect or use withRouter HOC.
React router 4 does not update view on link, but does on refresh
I recently reconfigured my React app, and it seems that I have broken the functionality of my parameterized routes. Rather than going too deep into how it used to work, I'll describe what I am currently stuck with and what I am aiming to achieve...
In a navigation panel on the side of my page, a user can view a list of links to resources - the urls for these links would be something like:
user/123
user/456
user/789
group/123
group/456
group/789
Clicking the first link will now render the User component in the main div on my page (rendering in {this.props.children} - see App.jsx below), and a call to componentDidMount() pulls data for the user with id 123 and populates the component. Ok, so far, so good.
However, if a user now clicks on the link for user/456, nothing happens. The url in the navbar changes, but the User component does not re-render, though clicking a link for a group will correctly clear out the User component and render the Group component instead... then, of course, I have the same problem with the groups, etc...
How can I force the re-rendering of a component when the pathname remains the same but the parameter has changed? If I've clicked the link for user/123 and then I click the link for user/456, I want that User component to re-render and pull the new data.
Other relevant code...
index.js
import { Router, Route, browserHistory } from 'react-router';
<Router history={browserHistory}>
<Route path="/" component={App}>
<Route path="/user/:id" component={User} />
<Route path="/group/:id" component={Group} />
</Route>
</Router>
App.jsx
<div className="App">
<div className="body_section_container">
<div className="body_section main">
<Nav />
{this.props.children}
</div>
<div className="body_section sidebar">
<Search searchModel={this.searchAll} user_list={this.state.user_list} group_list={this.state.group_list} organizations_list={this.state.organizations_list} />
</div>
</div>
</div>
Try plugging into componentWillReceiveProps, which should fire when the route params change. Then you can receive the new route params, submit a new query, and update state.
In the Route component, specify a key.
<Route path={YOURPATH} render={(props) => <YourComp {...props} keyProp={id} key={id}/>} />
when react see a different key, it will trigger rerender.