Reputation: 3117
I use Router like the code below to switch between components includes Home, Sidebar, Detail, Category.
For the first load, It will display all recent posts, featured posts. When I click a post to go detail It work well. In detail page, I click to render new post but It does not render.
My code:
class App extends React.Component {
render () {
// if ('serviceWorker' in navigator) {
// const registration = runtime.register();
// }
module.hot.accept();
$("body").removeClass();
$("body").addClass("homepage");
<Helmet>
<title>{blog_title}</title>
<meta name="description" content={blog_description} />
</Helmet>
const {feature, main, sidebar, mainbottom, header } = this.props;
return (
<div id="all">
{header}
<div className="container">
<div className="main-feature" id="main-feature">
{feature}
</div>
</div>
<div id="content" className="site-content">
<div className="container">
<div className="divcontainer">
<div id="po-homepage" className="content-area">
{main}
</div>
<aside id="secondary" className="sidebar widget-area">
<div className="popular_post" id="popular_post">
{sidebar}
</div>
</aside>
</div>
</div>
</div>
<div className="cat-list">
<div className="container">
<div className="thecategories" id="thecategories">
{mainbottom}
</div>
</div>
</div>
</div>
)
}
}
Please see photo:
My detail post:
import React, {Component} from "react"; //nếu chỉ import React thì sẽ cần React.Component
import $ from 'jquery';
import { Helmet } from "react-helmet";
import {
BrowserRouter as Router,
Route,
Switch
} from 'react-router-dom';
class Detail extends React.Component {
constructor(props) {
super(props);
this.state = {
post: {}
};
}
componentDidMount() {
//scroll to detail
$(document).ready(function(){
$("html, body").animate({ scrollTop: $("#content").offset().top - 100 }, 500);
$("body").removeClass();
$("body").addClass("detailpage");
});
console.log(this.props.detailink);
var that = this;
var url = window.location.href.split("/");
var slug = url.pop() || url.pop();
// console.log(CelestialSettings.URL.api + "/posts?slug=" + slug);
fetch(CelestialSettings.URL.api + "/posts?slug=" + slug)
.then(function(response) {
if (!response.ok) {
throw Error(response.statusText);
}
return response.json();
})
.then(function(res) {
that.setState({ post: res[0] });
});
}
renderPosts() {
return (
<div className="card">
<div className="card-body">
<Helmet>
<title>{this.state.post.title.rendered}</title>
<meta name="description" content={this.state.post.excerpt.rendered.replace(/<\/?[^>]+(>|$)/g, "")} />
</Helmet>
<h1 className="card-title">{this.state.post.title.rendered}</h1>
<p className="card-text">
<small className="text-muted">
{this.state.post.author_name} –{" "}
{this.state.post.published_date}
</small>
</p>
{this.state.post.featured_image_src ? (
<img
className="featured-image"
src={this.state.post.featured_image_src}
alt="featured image"
/>
) : null}
<p
className="card-text"
dangerouslySetInnerHTML={{
__html: this.state.post.content.rendered
}}
/>
</div>
</div>
);
}
renderEmpty() {
return (
<div></div>
);
}
render() {
var href = window.location.href+"/?view=react";
return (
<div className="container post-entry">
{this.state.post.title ? this.renderPosts() : this.renderEmpty()}
</div>
);
}
}
export default Detail;
As I added ComponentDidUpdate(), it works but it renders loop back top first detail before render new post. I use like this.
componentDidUpdate() {
var that = this;
var url = window.location.href.split("/");
var slug = url.pop() || url.pop();
console.log(CelestialSettings.URL.api + "/posts?slug=" + slug);
fetch(CelestialSettings.URL.api + "/posts?slug=" + slug)
.then(function(response) {
if (!response.ok) {
throw Error(response.statusText);
}
return response.json();
})
.then(function(res) {
that.setState({ post: res[0] });
});
}
Any suggestion for my issue? Thank you so much.
Upvotes: 1
Views: 210
Reputation: 195971
Your Detail
component needs to be aware that the url changed and since no relevant properties are passed when you try to render it you must use the withRouter
HOC provided by react router
so at the top of your Details component file
import {
BrowserRouter as Router,
Route,
Switch,
withRouter // add this (and the comma in the above line)
} from 'react-router-dom';
and at the bottom where you export
it use
export default withRouter(Detail);
Update
Reading your Details
code a bit more i see that you fetch
the content on the ComponentDidMount
lifecycle event, but when you load a new post you remain to the same page so the Details
component is not unmounted/remounted, it is just updated with new props and so the ComponentDidMount
does not fire again. You will have to also use ComponentDidUpdate
Some additional info
Since you are using the react-router you should not be parsing the window.location.href
yourself trying to figure the slug. You can automatically pass it down from the props provided by the router.
<!-- language: lang-js -->
<Route
path={CelestialSettings.path + 'posts/:slug'}
render={(props) => <App header={<Header/>}
main={<Detail slug={props.match.slug} detailink={CelestialSettings.path + 'posts/:slug'} />}
sidebar={<Popular />}
feature={<Featurex/>}
mainbottom={<Categories/>} />} />
Then in your ComponentDidUpdate
<!-- language: lang-js -->
componentDidUpdate(newProps) {
const {slug} = newProps;
const that = this; // you do not need this if you use arrow functions
if (slug !== this.props.slug) {
fetch(CelestialSettings.URL.api + "/posts?slug=" + slug)
.then(function(response) {
if (!response.ok) {
throw Error(response.statusText);
}
return response.json();
})
.then(function(res) {
that.setState({
post: res[0]
});
});
}
}
Upvotes: 2