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.
Related
I have a render function inside App.js.
return (
<div className="container">
<h1 className="text-center main-title">Activity Logger</h1>
<Router>
<NavigationBar />
<Route exact path="/">
{this.renderForm()}
</Route>
<Route path="/activitydisplay">{this.renderTable()}</Route>
</Router>
</div>
);
}
The Router is a BrowserRouter. The functions it call are
renderForm = () => {
if (this.state.formDataError) {
return "Error loading data";
}
console.log("renderForm was called");
return (
<div className="mt-3">
<ActivityForm
machinesList={this.state.machinesList}
operatorsList={this.state.operatorsList}
onFormSubmit={this.postFormData}
postSuccessCount={this.state.postSuccessCount}
loggedOperator={this.props.cookies.get("logger") || null}
/>
</div>
);
};
renderTable() {
if (this.state.tableDataError) {
return "Error loading data";
}
return (
<div className="mt-3">
<ActivityDisplay
activityData={this.state.activityData}
machines={this.state.machinesList}
operators={this.state.operatorsList}
editDataHandler={this.editData}
deleteDataHandler={this.deleteData}
/>
</div>
);
}
The components are remounted when I switch between the routes at the front end. To troubleshoot, I put logging in renderForm function and in the ActivityForm constructor. I can see the logging from renderForm only when the App component is mounted and not when I switch between routes. However, I can see the logging from ActivityForm constructor whenever I switch between the components at the front end using a navigation bar.
I lose all the states I had in the component because of this. The behavior I expect is that the component should not remount when I switch tabs. Can anyone please help?
React Router does basically update the UI according to a specific state. They handle that in a very organized manner with Browser Navigation events like Back, Refresh & so on.
As we all know, when React change the UI according to a specific updated state, it will mount & unMount relevant components accordingly. https://reacttraining.com/blog/mount-vs-render/ article explains that concept very nicely.
That same behavior will apply to React Router also. When you navigate to different path, existing components can be unMount & new components can be mount according to your code.
Route with render prop
return (
<div className="container">
<h1 className="text-center main-title">Activity Logger</h1>
<Router>
<NavigationBar />
<Router>
<Route path="/activitydisplay" render={() => this.renderTable()} />
<Route exact path="/" render={() => this.renderForm()} />
</Router>
</Router>
</div>
);
Note - It's better if you can pass the component into Route using render prop. That will make renderForm or renderTable to run before ActivityForm or ActivityDisplay mount. It will also make sure that to pass all the updated props correctly to the components where necessary.
Let me know if you need further support.
I am new to react. I am using react to list stores, when the user clicks on the store it will show just the menu and nothing else. The problem that I am facing is that everytime i click on a store the menu is shown right below the list of stores. I want it to be rendered as a new page with nothing but the menu. Is there a way i can do that with react router? Below is my code. Any help would be really appreciated
<Router>
<div> {stores.map((store) => (
<div><Link to="/storemenu"> {store} </Link> <br/></div>
))} <button onClick={()=>addStore()}>Add Store</button>
</div>
<Switch>
</Switch>
<Route exact path="/storemenu">
<StoreMenu/>
</Route>
</Router>
By rendering whatever outside of all Routes you automatically make it appear site-wide (on all routes).
If you don't want list of stores to appear on all pages - just render it inside some other Route.
If, on the other hand, you want list of stores to appear on some pages but not on some others (like /storemenu), render list of stores inside a Route & pass to this route an array of paths where list of stores should appear as path.
Then write another Switch inside this Route & handle other routing logic as needed.
Here is a minimalistic example that works:
import React from "react";
import { BrowserRouter as Router, Switch, Route, Link } from "react-router-dom";
const App = () => {
const store = ["Berlin", "Moscow"];
return (
<>
<Router>
<nav> { /* This nav is visible on all pages */ }
<Link to="/some">
/some
</Link>
<Link to="/pages">
/pages
</Link>
<Link to="/storemenu">
/storemenu
</Link>
</nav>
<Switch>
<Route exact path={["/", "/some", "/pages"]}>
{ /* Stores are visible only on "/", "/some" & "/pages" */ }
{store.map((store) => {
return store;
})}
<Switch>
<Route path="/some">
A page with list
</Route>
<Route path="/pages">
Another page with list
</Route>
</Switch>
</Route>
<Route exact path="/storemenu">
Here is Store Menu with no stores list!
</Route>
</Switch>
</Router>
</>
);
};
export default App;
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>
)}};
I am learning React.js. I am using React router version 4.3.1 and React router dom 4.3.1.
My current app structure is given below.
<React.Fragment>
{/* sidebar component */}
<SideBar store = {Mystore} />
<div className="main-panel">
{/* navbar component */}
<NavBar store = {Mystore}/>
{/* content component */}
<Content store = {Mystore}/>
{/* footer component */}
</div>
</React.Fragment>
in my content component i have a Router set up as given below.
<BrowserRouter>
<React.Fragment>
<Route exact path="/phoenix/public" component={Login} />
<ProtectedRoute exact path="/phoenix/public/sample" component={SamplePage} />
<ProtectedRoute exact path="/phoenix/public/view_ticket" component={ViewTicket} />
<ProtectedRoute exact path="/phoenix/public/create_ticket" component={CreateTicket} />
<ProtectedRoute exact path="/phoenix/public/dashboard" component={Dashborad} />
</React.Fragment>
</BrowserRouter>
Here ProtectedRoute is a functional component which checks if the user is authenticated and returns a Route
My sidebar has some a tags like below.
<li>
<a href="dashboard">
<i className="ti-panel" />
<p>Dashboard</p>
</a>
</li>
My goal is to navigate to different Routes without page refresh on click from side bar. But clicking href reloads the page and all my state in my Mobx refreshes. I have tried to Redirect but could not get the Router which is stated in siblings component (content is siblings of sidebar). I have no idea how to achieve this.
As stated in the comments on your question, regular <a> tags will not work, and refresh the page. So you will need to use Link or NavLink.
If you want to make it work in your sidebar (child component) you will need to wrap the <BrowserRouter> around all the components where you want to use the routes.
For example like this (using your code):
<BrowserRouter>
<React.Fragment>
{/* sidebar component */}
<SideBar store = {Mystore} />
<div className="main-panel">
{/* navbar component */}
<NavBar store = {Mystore}/>
{/* content component */}
<Content store = {Mystore}/>
{/* footer component */}
</div>
</React.Fragment>
</BrowserRouter>
And your Sidebar will be something like this:
<React.Fragment>
<Link to="/dashboard">Dashboard</Link>
...
</React.Fragment>
Then in your content component. You need to delete the <BrowserRouter>
I fixed my problem by having BrowserRouter like below as suggested by #PreDinnerSnack.
<BrowserRouter>
<React.Fragment>
{/* sidebar component */}
<SideBar store = {Mystore} />
<div className="main-panel">
{/* navbar component */}
<NavBar store = {Mystore}/>
{/* content component */}
<Content store = {Mystore}/>
{/* footer component */}
</div>
</React.Fragment>
</BrowserRouter>
Then i used Navlink in My Sidebar component. Which fixed the problem of navigating to the desired route without page refresh. But then i faced another problem as Content was not Refreshing according to the route.
For that problem I found the observer HOC of Mobx was interfering with my router. I had to wrap my content with withRouter as below to fix this problem.
withRouter(observer(Content));
I hope my solution will come in handy for someone as i found no tutorial with the examples of React Router implementation like mine on the web.
I have an AngularJS background, and started to play around with React. I'm using the react-router and want have basically this setup:
Template
export default ( props ) => {
return (
<div>
<Navbar></Navbar>
{ props.children }
</div>
);
}
Routing in App.js
<Router>
<Route path="/" component={Template}>
<IndexRoute component={RestaurantRoulette} />
<Route name="workspace" path="workspace(/:workspace_id)" component={RestaurantRoulette}>
<Route name="run" path="run" component={Run}></Route>
</Route>
</Route>
</Router>
Navbar
<ul className="navigation">
<li>RestaurantRoulette</li>
<Link to="run">Run</Link>
<li>Save</li>
</ul>
What I want
When I am in localhost:8080/workspaces/444 I want to click on the Run Link, and navigate to localhost:8080/workspaces/444/run
What is the current status
If I manually type the url localhost:8080/workspaces/444/run, everything works fine. But when I click on the Link in the navbar, I get to localhost:8080/run.
Can somebody tell me how this works? How this is all connected?
I think the Link tag should be like that:
<Link to={'workspaces/' + this.props.workspace_id + '/run'}>Run</link>
you should get the workspace_id from path in Template, and pass it to the NavBar component