Hai Tien
Hai Tien

Reputation: 3117

React router not rendered with second click

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:

enter image description here

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} &ndash;{" "}  
              {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

Answers (1)

Gabriele Petrioli
Gabriele Petrioli

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

  1. 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/>} />} />
    
  2. 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

Related Questions