I have been learning react for past few weeks. And now I'm having an issue that when I reload the page using browser reload button, instead of reloading the component , the component just disappear (and is blank. No error is visibly thrown, but not even the component that's supposed to render on that route shows up.) while other header/footer component loads fine. While same type up set up on other link on nav bar, reload is just working fine.
[1]: https://react---frontend.herokuapp.com/ this is the link for my dummy react website.
Here in this page we can see some post. Clicking on the post takes the user to post details page.
[2]: https://react---frontend.herokuapp.com/post/ (this link doesn't load directly, it's just for refrence)
Now here inserting post specific comment is just working fine and it shows up instantly without reloading the page. But when reload button is pressed the post detail component just disappear.
This is my Index.js
ReactDOM.render(
<Provider store = {store} >
<BrowserRouter >
<PersistGate persistor={persistor}>
<App />
</PersistGate>
</BrowserRouter>
</Provider>,
document.getElementById('root'));
This is my App.js
render(){
return (
<div className="App">
<Header />
<Navbar title = "React Blog" />
<Body />
<Footer />
</div>
);
}
This is my body.js. These routes support browser reload.
return (
<div>
<Router>
<Switch>
<Route exact path = "/" component = {PostIndex} />
<Route path = "/contact" component = {ContactIndex} />
<Route path = "/about" component = {AboutIndex} />
<Route path = "/auth" component = {AuthIndex} />
</Switch>
</Router>
</div>
);
This is postindex.js. The showpost component is the culprit.It doesnot load when page is reloaded.
return(//showpost should have been loaded when refreshed
<div>
<Router>
<Switch>
<Route path = "/" exact component = {Post} />
<Route path = "/post" component = {Showpost} />
</Switch>
</Router>
</div>
);
This is showpost.js
render() { //this page is no re-rendering when refreshed
const comment = this.props.post.comment.map(function(comment){
return <div key = {comment.id}>{comment.body}</div>
})
console.log(this.props.post)
return(
<div className='container'>
<div><h3>{this.props.post.title}</h3></div>
<div>{this.props.post.body}</div><hr/>
<h3><label>Comment</label></h3>
<CreateComment/>
<hr/>
{comment}
</div>
);
}
For every switch I have wrapped with BrowserRouter. Is that a usual way to do it? As for state I am using redux-persist.
And how can I make https://react---frontend.herokuapp.com/post/id
load directly using url.
You only need to wrap with Router in the root
import React from 'react';
import ReactDOM from 'react-dom';
import {BrowserRouter as Router} from 'react-router-dom';
import './index.css';
import App from './App';
ReactDOM.render(
<Router>
<App />
</Router>
, document.getElementById('root'));
Related
I'm using React Router with a generic Page component that grabs the content from the WP Rest API based on the slug. I can visit the pages directly via url and routing works.
I have a small menu in my Footer component that use the React Router <Link> component linking to the various pages using the Page component.
When I click these links, the url is updating but the Page component is not re-rendering with the updated slug and thus not grabbing the correct page content.
This is definitely update blocking as described in the React Router docs here: https://reacttraining.com/react-router/web/guides/dealing-with-update-blocking
That covers most of the solutions however in my case the Footer component is not a Route and is a child of the Layout component which is also route-less and wraps all other components.
To fix, the docs suggest that I would need to send the React Router location prop down the component tree.
I've tried exporting all of the components using withRouter down the component tree which should pass this prop down but it isn't working.
How can I get the <Page> component to update when I click on a <Link> in the <Footer> component?
Routes in App.js:
<BrowserRouter>
<Layout
addToCart={addToCart}
removeFromCart={removeFromCart}
clearCart={clearCart}
cart={cart}
catalog={catalog}
>
<Switch>
<Route exact path="/" render={() => <Home catalog={catalog} />} />
<Route
exact
path="/catalog"
render={() => (
<Catalog addToCart={addToCart} catalog={catalog} />
)}
/>
<Route path="/:slug" render={props => <Page {...props} />} />
<Route
path="/catalog/:category/:slug"
render={() => (
<SingleProduct addToCart={addToCart} catalog={catalog} />
)}
/>
</Switch>
</Layout>
</BrowserRouter>
Layout.js (simplified):
<Layout>
<Header />
{children}
<Footer />
<Layout
Links in <Footer />:
<ul className="nostyle footer-links">
<li>
<Link to="/faq">FAQ</Link>
</li>
<li>
<Link to="/about">About</Link>
</li>
<li>
<Link to="/contact">Contact Us</Link>
</li>
</ul>
//...
export default withRouter(Footer)
Page.js:
const Page = props => {
const slug = props.match.params.slug
const [page, setPage] = useState()
const fetchPage = async () => {
const result = await axios(
`${params.liveUrl}${params.pages}?slug=${slug}`
)
setPage(result.data)
}
useEffect(() => {
fetchPage()
}, [])
return (
<Fragment>
<div className="page" key={page[0].slug}>
{page[0].content}
</div>
</Fragment>
)
}
export default withRouter(Page)
So if I visit /faq or /about directly, it loads the <Page /> component as directed in the Route, passing in the slug and everything displays properly.
But, if I click one of the links in <Footer />, the url changes but the <Page /> component is not refreshed with the new slug and thus the correct page data. If I reload the page, the correct data is loaded.
How can I force the <Page> component to update when the Route is changed?
Note that I am using React hooks and not using Redux.
Thanks!
You need to setup the unique key for your Page component, so a new instance of this component would render every time a key changes.
Assuming your slug is unique, you can do the following:
<Page key={props.match.params.slug} {...props} />
Alternatively, you can also do it like this:
<Page key={window.location.pathname} {...props} />
I'm new to React Router so if this has been asked before maybe someone could point me in the right direction! Basically I have a WordPress install that I'm pulling in my websites data from through the API.
I've created custom routes to query my pages and my posts by slug.
Using react router I was able to create a template called Page.js which changes dynamically using the code below.
However, now I'm trying to do the same exact thing with the blog posts but the app isn't using Blog.js its still defaulting back to Page.js
here's my App.js code...
import React from "react";
import { BrowserRouter as Router, Route } from "react-router-dom";
import Home from './pages/Home';
import Page from './pages/Page';
import Blog from './pages/Blog';
import Header from './components/Header';
import Footer from './components/Footer';
class App extends React.Component {
render() {
return (
<Router>
<div>
<Header/>
<Route exact path="/" component={Home} />
<Route path="/:slug" component={Page} />
<Route path="/blog/:slug" component={Blog} />
<Footer/>
</div>
</Router>
);
}
}
export default App;
More Details:
Page.js works by checking const { slug } = this.props.match.params; and then querying WordPress using that slug to pull in the data it needs. In componentDidUpdate i'm checking prevProps to see if the slug matches the previous slug, if not it fetching the new data.
This works great and I was hoping to do the same in the Blog.js as well.
However, if this isn't the best approach please advise another method.
Two things:
Use element: This will allow only one route to be used, no composing. (See this documentation)
Check the order of path statements: Use defined paths before :param, this avoids considering /blog/:slug as a /:slug parameter.
`
import { BrowserRouter as Router, Route, Switch } from "react-router-dom";
class App extends React.Component {
render() {
return (
<Router>
<div>
<Header/>
<Switch>
<Route exact path="/" component={Home} />
<Route path="/blog/:slug" component={Blog} />
<Route path="/:slug" component={Page} />
</Switch>
<Footer/>
</div>
</Router>
);
}
}
I think you're pretty close to the recommended implementation, just a few small tweaks should get you there.
First,
In your App.js file you're actually handling routing, without using the <Switch> component provided by React Router, replacing the <div> and </div> tags in your App.js file with <Switch> and </Switch> respectively should get this working for you. See below...
import React from "react";
import { BrowserRouter as Router, Route, Switch } from "react-router-dom"; //make sure you import it also!
import Home from './pages/Home';
import Page from './pages/Page';
import Blog from './pages/Blog';
import Header from './components/Header';
import Footer from './components/Footer';
class App extends React.Component {
render() {
return (
<Router>
<Switch> //Add this in
<Header />
<Route exact path="/" component={Home} />
<Route path="/blog/:slug" component={Blog} />
<Route path="/:slug" component={Page} />
<Footer />
</Switch> //Add this in
</Router>
);
}
}
export default App;
I would recommend going further though!
To make these components more understandable, you should refactor routing functionality into a routes.js file, and top-level App component logic/structure into the App.js file. See below...
In App.js:
This file is where you should handle your base application structure and logic. For example this file is where you'll import your <Header>, your <Footer>, and where the Route component will render.
import * as React from 'react'
import Header from './../Header/Header.jsx'
import Footer from './../Footer/Footer.jsx'
class App extends React.Component {
constructor(props) {
super(props)
this.state = {
// Handle your top-level application state here
}
}
// define your top-level application functions here
render() {
return (
<div>
<Header />
<main>
{this.props.children} //This where the Route components will render
</main>
<Footer />
</div>
)
}
}
export default App
In Routes.js:
This file is where you should import your App component, and then handle the routing statements.
import React from 'react'
import { BrowserRouter as Router, Route, Switch } from 'react-router-dom'
import App from './components/App'
import Home from './pages/Home'
import Page from './pages/Page'
import Blog from './pages/Blog'
/* construct routes */
export default () => {
return (
<Router>
<App>
<Switch>
<Route path='/' exact component={Home} />
<Route path='/blog/:slug' component={Blog} />
<Route path='/:slug' component={Page} />
</Switch>
</App>
</Router>
)
}
If you structure your application this way, your routing logic and top-level application logic are separate, and in the end your files will be less cluttered as both Route files and top-level App files can get fairly dense.
Hope this helps! Let me know if I can explain anything further.
I am using react-router and having some difficulties with it's behaviour.
The Nav shows on all pages as desired. However, the Profile shows on all pages too. I only want to show this on /home and also on the /music and /players pages, which it does. However, it also shows on the /charts page which is confusing me.
My code looks like the following.
import React from 'react';
import { Route } from 'react-router-dom'
import Nav from './components/Nav'
import Profile from './components/Profile'
import Players from './components/Players'
import Music from './components/Music'
import Charts from './components/Charts'
const App = () => {
return (
<section>
<Nav />
<Route path="/home">
<div>
<Profile avatarUrl={ avatarUrl }/>
<Route path="/players" component={Players}/>
<Route path="/music" component={Music}/>
</div>
</Route>
<Route path="/charts" component={Charts}/>
</section>
)
}
export default App;
I have read through the docs, tried putting in a Switch component, added exact to the home route but this leads to other unexpected behaviour.
Can anyone advise what I am doing wrong?
Thanks Pete!
Try this:
import React from 'react';
import { Route, BrowserRouter as Router } from 'react-router-dom'
import Nav from './components/Nav'
import Profile from './components/Profile'
import Players from './components/Players'
import Music from './components/Music'
import Charts from './components/Charts'
const Home = ({match}) => {
return (
<div>
<Profile avatarUrl={ avatarUrl }/>
<Route path=`${match.url}/players` component={Players}/>
<Route path=`${match.url}/music` component={Music}/>
</div>
);
};
const App = () => {
return (
<section>
<Nav />
<Router>
<Switch>
<Route path="/charts" exact={true} component={Charts}/>
<Route path="/home" component={Home} />
</Switch>
</Router>
</section>
)
}
export default App;
I haven't tested this, but this should work.
Assuming that you're using react-router v4, I don't know if you can actually use your home route in the way you've used it.
In the code above, Switch basically renders the first match between the routes specified under it. The exact keyword will ensure that only /charts path will display the Charts component.
The Home component will render in any path that starts with /home.
Now, for path /home/players, you'll see the Profile and the Players component, whereas for path /home/music, you'll see the other combination.
Hope this helps. :)
Edit:
Added Router to the code.
Edit:
Working code available here: https://codesandbox.io/s/8x9pql9m19
Change route on right hand side to:
/home
/home/players
/home/music
/charts
I installed react-router-dom and use this code for routing, But i have error :
import React from 'react';
import ReactDOM from 'react-dom';
import { Route, Switch } from 'react-router-dom';
class Home extends React.Component{
render(){
return(
<h1>Home</h1>
);
}
}
class About extends React.Component{
render(){
return(
<h1>About</h1>
);
}
}
ReactDOM.render(
<Switch>
<Route exact path='/' component={Home}/>
<Route path='/about' component={About}/>
</Switch>,
document.getElementById('main')
);
What's the right way for routing in reactjs ?
tnx
Wrap BrowserRouter around your Switch like below,
<BrowserRouter>
<Switch>
<Route exact path='/' component={Home} />
<Route path='/about' component={About} />
</Switch>
</BrowserRouter>
Here is the working code demo in codesandbox.
You didn't import BrowserRouter
You should wrap your <Switch> arround <BrowserRouter> tag
Better use a component than trying to render a <Switch> element
You may find anything your looking for on this link :
https://reacttraining.com/react-router/web/guides/philosophy
Also i made a quick pen : https://codepen.io/FabienGreard/pen/KZgwKO?editors=1010
Kay Concepts
<BrowserRouter> is needed because
Each router creates a history object, which it uses to keep track of the current location and re-render the website whenever that changes
A React Router component that does not have a router as one of its ancestors will fail to work.
Router components only expect to receive a single child element. To work within this limitation, it is useful to create an component that renders the rest of your application.
<Route>
The component is the main building block of React Router. Anywhere that you want to only render content based on the location’s pathname, you should use a element.
<Path>
When the current location’s pathname is matched by the path, the route will render a React element.
<Switch>
You can use the component to group s.
The will iterate over its children elements (the routes) and only render the first one that matches the current pathname.
I think you should create different component for Routes.
I'll just explain general project structure here
You can create component to hold <Header> and <MainContent>
As <Header> will be same througout the application and it will not change if path changes. You can include routes in <MainContent> which will be updated if path changes.
MainContent.js
import { Switch, Route } from 'react-router-dom';
const MainContent = () => (
<main>
<Switch>
<Route exact path='/' component={Home}/>
<Route path='/about' component={About}/>
</Switch>
</main>
)
export default MainContent;
Layout.js
class Layout extends Component {
render() {
return (
<div className={classes.root}>
<Header />
<MainContent />
</div>
);
}
Now you can use <BrowserRouter>to wrap your <Layout> in App.js . or you can use it in <MainContent> as well
App.js
import { BrowserRouter } from "react-router-dom";
class App extends Component {
render() {
return(
<BrowserRoter>
<Layout />
</BrowserRouter>
);
}
}
I tried to move BrowserRouter out of my component. My App looked like this:
class App extends Component {
constructor(props) {
super(props);
this.state = {
}
}
render() {
return (
<BrowserRouter>
<main>
<Menu />
<Switch>
<Route exact path="/about" component = {About} />
<Route exact path="/admin" component = {BooksForm} />
<Route exact path="/cart" component = {Cart} />
<Route exact path="/" component = {BookList} />
</Switch>
<Footer />
</main>
</BrowserRouter>
);
}
}
And everything was working fine. But when I pulled BrowserRouter up, so my index.js would look like this:
const renderApp = () => (
<Provider store={createStoreWithMiddleware(reducers)}>
<BrowserRouter>
<App/>
</BrowserRouter>
</Provider>
)
const root = document.getElementById('app')
render(renderApp(), root)
it stopped working. When I click on one of the links the url changes but there's no change in my app. It renders new componennt only if I reload the page. How can I make it work without placing router component in the same component as Switch?
Tough to tell without looking at the rest of the code. Are you using the proper react-router <Link>s? I assume you don't have the <BrowserRouter> element in both components, can't imagine that nesting them would do any good.
I'm on an old version of react-router, so I'm seeing some of these examples for the first time, but it looks like you don't need the exact keyword on all of those <Route>s within <Switch> -- the switch guarantees that only one of them will math.
Allright I've made it working again. Connect from redux was causing the problem and withRouter solved it