React router Link not causing component to update within nested routes

react-router blocked updates
react-router url changes but component does not load
react-router redirect
react router params
react-router not updating
react router reload component
react router v4
react router 5

This is driving me crazy. When I try to use React Router's Link within a nested route, the link updates in the browser but the view isn't changing. Yet if I refresh the page to the link, it does. Somehow, the component isn't updating when it should (or at least that's the goal).

Here's what my links look like (prev/next-item are really vars):

<Link to={'/portfolio/previous-item'}>
    <button className="button button-xs">Previous</button>
</Link>
<Link to={'/portfolio/next-item'}>
    <button className="button button-xs">Next</button>
</Link>

A hacky solution is to manaully call a forceUpate() like:

<Link onClick={this.forceUpdate} to={'/portfolio/next-item'}>
    <button className="button button-xs">Next</button>
</Link>

That works, but causes a full page refresh, which I don't want and an error:

ReactComponent.js:85 Uncaught TypeError: Cannot read property 'enqueueForceUpdate' of undefined

I've searched high and low for an answer and the closest I could come is this: https://github.com/reactjs/react-router/issues/880. But it's old and I'm not using the pure render mixin.

Here are my relevant routes:

<Route component={App}>
    <Route path='/' component={Home}>
        <Route path="/index:hashRoute" component={Home} />
    </Route>
    <Route path="/portfolio" component={PortfolioDetail} >
        <Route path="/portfolio/:slug" component={PortfolioItemDetail} />
    </Route>
    <Route path="*" component={NoMatch} />
</Route>

For whatever reason, calling Link is not causing the component to remount which needs to happen in order to fetch the content for the new view. It does call componentDidUpdate, and I'm sure I could check for a url slug change and then trigger my ajax call/view update there, but it seems like this shouldn't be needed.

EDIT (more of the relevant code):

PortfolioDetail.js

import React, {Component} from 'react';
import { browserHistory } from 'react-router'
import {connect} from 'react-redux';
import Loader from '../components/common/loader';
import PortfolioItemDetail from '../components/portfolio-detail/portfolioItemDetail';
import * as portfolioActions  from '../actions/portfolio';

export default class PortfolioDetail extends Component {

    static readyOnActions(dispatch, params) {
        // this action fires when rendering on the server then again with each componentDidMount. 
        // but not firing with Link...
        return Promise.all([
            dispatch(portfolioActions.fetchPortfolioDetailIfNeeded(params.slug))
        ]);
    }

    componentDidMount() {
        // react-router Link is not causing this event to fire
        const {dispatch, params} = this.props;
        PortfolioDetail.readyOnActions(dispatch, params);
    }

    componentWillUnmount() {
        // react-router Link is not causing this event to fire
        this.props.dispatch(portfolioActions.resetPortfolioDetail());
    }

    renderPortfolioItemDetail(browserHistory) {
        const {DetailReadyState, item} = this.props.portfolio;
        if (DetailReadyState === 'WORK_DETAIL_FETCHING') {
            return <Loader />;
        } else if (DetailReadyState === 'WORK_DETAIL_FETCHED') {
            return <PortfolioItemDetail />; // used to have this as this.props.children when the route was nested
        } else if (DetailReadyState === 'WORK_DETAIL_FETCH_FAILED') {
            browserHistory.push('/not-found');
        }
    }

    render() {
        return (
            <div id="interior-page">
                {this.renderPortfolioItemDetail(browserHistory)}
            </div>
        );
    }
}

function mapStateToProps(state) {
    return {
        portfolio: state.portfolio
    };
}
function mapDispatchToProps(dispatch) {
    return {
        dispatch: dispatch
    }
}

export default connect(mapStateToProps, mapDispatchToProps)(PortfolioDetail);

PortfolioItemDetail.js

import React, {Component} from 'react';
import {connect} from 'react-redux';
import Gallery from './gallery';

export default class PortfolioItemDetail extends React.Component {

    makeGallery(gallery) {
        if (gallery) {
            return gallery
                .split('|')
                .map((image, i) => {
                    return <li key={i}><img src={'/images/portfolio/' + image} alt="" /></li>
            })
        }
    }

    render() {
        const { item } = this.props.portfolio;

        return (
            <div className="portfolio-detail container-fluid">
                <Gallery
                    makeGallery={this.makeGallery.bind(this)}
                    item={item}
                />
            </div>
        );
    }
}

function mapStateToProps(state) {
    return {
        portfolio: state.portfolio
    };
}

export default connect(mapStateToProps)(PortfolioItemDetail);

gallery.js

import React, { Component } from 'react';
import { Link } from 'react-router';

const Gallery = (props) => {

    const {gallery, prev, next} = props.item;
    const prevButton = prev ? <Link to={'/portfolio/' + prev}><button className="button button-xs">Previous</button></Link> : '';
    const nextButton = next ? <Link to={'/portfolio/' + next}><button className="button button-xs">Next</button></Link> : '';

    return (
        <div>
            <ul className="gallery">
                {props.makeGallery(gallery)}
            </ul>
            <div className="next-prev-btns">
                {prevButton}
                {nextButton}
            </div>
        </div>
    );
};

export default Gallery;

New routes, based on Anoop's suggestion:

<Route component={App}>
    <Route path='/' component={Home}>
        <Route path="/index:hashRoute" component={Home} />
    </Route>
    <Route path="/portfolio/:slug" component={PortfolioDetail} />
    <Route path="*" component={NoMatch} />
</Route>

Could not get to the bottom of this, but I was able to achieve my goals with ComponentWillRecieveProps:

componentWillReceiveProps(nextProps){
    if (nextProps.params.slug !== this.props.params.slug) {
        const {dispatch, params} = nextProps;
        PortfolioDetail.readyOnActions(dispatch, params, true);
    }
}

In other words, for whatever reason when I use React Router Link to link to a page with the SAME PARENT COMPONENT, it doesn't fire componentWillUnMount/componentWillMount. So I'm having to manually trigger my actions. It does work as I expect whenever I link to Routes with a different parent component.

Maybe this is as designed, but it doesn't seem right and isn't intuitive. I've noticed that there are many similar questions on Stackoverflow about Link changing the url but not updating the page so I'm not the only one. If anyone has any insight on this I would still love to hear it!

nested routes keep rerendering � Issue #6818 � ReactTraining/react , Version Please replace this sentence with the React Router version that you are using. Go to codesanbox demo, click on Nested Tab A link, then click on Nested Tab It should not rerender "parent" components of nested routes This is causing a delay of 2/3 seconds in our app, when moving from one� match props from react-router provides the current url through match.url; match.url can be used to construct the nested routes. Here we construct the nested routes using each user tabs info from the data. match.path provides the pathname. This will be used to define the nested route schema. Why to use match.path instead of match.url?

It's good to share the components code also. However, I tried to recreate the same locally and is working fine for me. Below is the sample code,

import { Route, Link } from 'react-router';
import React from 'react';
import App from '../components/App';

const Home = ({ children }) => (
  <div>
    Hello There Team!!!
    {children}
  </div>
);

const PortfolioDetail = () => (
  <div>
    <Link to={'/portfolio/previous-item'}>
      <button className="button button-xs">Previous</button>
    </Link>
    <Link to={'/portfolio/next-item'}>
      <button className="button button-xs">Next</button>
    </Link>
  </div>
);

const PortfolioItemDetail = () => (
  <div>PortfolioItemDetail</div>
);

const NoMatch = () => (
  <div>404</div>
);

module.exports = (
  <Route path="/" component={Home}>
    <Route path='/' component={Home}>
        <Route path="/index:hashRoute" component={Home} />
    </Route>
    <Route path="/portfolio" component={PortfolioDetail} />
    <Route path="/portfolio/:slug" component={PortfolioItemDetail} />
    <Route path="*" component={NoMatch} />
  </Route>
);

React Router v4 is not rendering on nested routes : reactjs, This is driving me crazy. When I try to use React Router's Link within a nested route, the link updates in the browser but the view isn't changing. Yet if I refresh the� I have a component library that has the reference of Link from a Button component. The lib is then used in the apps which include the connected react router. I mainly use connected router to dispatch routes from sagas, while using Links in most of the cases from View components. I do have exceptions where from components I dispatch to router as

componentWillReceiveProps is the answer to this one, but it's a little annoying. I wrote a BaseController "concept" which sets a state action on route changes EVEN though the route's component is the same. So imagine your routes look like this:

<Route path="test" name="test" component={TestController} />
<Route path="test/edit(/:id)" name="test" component={TestController} />
<Route path="test/anything" name="test" component={TestController} />

So then a BaseController would check the route update:

import React from "react";

/**
 * conceptual experiment
 * to adapt a controller/action sort of approach
 */
export default class BaseController extends React.Component {


    /**
     * setState function as a call back to be set from
     * every inheriting instance
     *
     * @param setStateCallback
     */
    init(setStateCallback) {
        this.setStateCall = setStateCallback
        this.setStateCall({action: this.getActionFromPath(this.props.location.pathname)})
    }

    componentWillReceiveProps(nextProps) {

        if (nextProps.location.pathname != this.props.location.pathname) {
            this.setStateCall({action: this.getActionFromPath(nextProps.location.pathname)})
        }
    }

    getActionFromPath(path) {

        let split = path.split('/')
        if(split.length == 3 && split[2].length > 0) {
            return split[2]
        } else {
            return 'index'
        }

    }

    render() {
        return null
    }

}

You can then inherit from that one:

import React from "react"; import BaseController from './BaseController'

export default class TestController extends BaseController {


    componentWillMount() {
        /**
         * convention is to call init to
         * pass the setState function
         */
        this.init(this.setState)
    }

    componentDidUpdate(){
        /**
         * state change due to route change
         */
        console.log(this.state)
    }


    getContent(){

        switch(this.state.action) {

            case 'index':
                return <span> Index action </span>
            case 'anything':
                return <span>Anything action route</span>
            case 'edit':
                return <span>Edit action route</span>
            default:
                return <span>404 I guess</span>

        }

    }

    render() {

        return (<div>
                    <h1>Test page</h1>
                    <p>
                        {this.getContent()}
                    </p>
            </div>)
        }

}

React Router Nested Routes Code Along, React Router v4 is not rendering on nested routes ResourceUI component have 2 sections. the left section have links and right If it does it might be your component tree is not updating properly - this can be caused Dashboard in React. The Link component allows navigating the different routes on the websites, whereas NavLink component is used to add styles to the active routes. React Router Switch The Switch > component is used to render components only when the path will be matched .

I got stuck on this also in React 16.

My solution was as follows:

componentWillMount() {
    const { id } = this.props.match.params;
    this.props.fetchCategory(id); // Fetch data and set state
}

componentWillReceiveProps(nextProps) {
    const { id } = nextProps.match.params;
    const { category } = nextProps;

    if(!category) {
        this.props.fetchCategory(id); // Fetch data and set state
    }
}

I am using redux to manage state but the concept is the same I think.

Set the state as per normal on the WillMount method and when the WillReceiveProps is called you can check if the state has been updated if it hasn't you can recall the method that sets your state, this should re-render your component.

React Router v5: The Complete Guide, In the previous lesson, we saw how to have routes dynamically render state won't change the URL, meaning there is no way to provide a link directly to one Using nested React-Router, we can write our application so one component, the information available about the route, including the URL path that caused the� If I'm currently already on the "post" route, the URL will update but the content within the PostContent component won't change. That component fetches data from a Fluxxor store like this:

I am uncertain whether it fixes the original problem, but I had a similar issue which was resolved by passing in the function callback () => this.forceUpdate() instead of this.forceUpdate.

Since no one else is mentioning it, I see that you are using onClick={this.forceUpdate}, and would try onClick={() => this.forceUpdate()}.

Route component, Since <Route> , <Link> and all the other React Router APIs that we'll basic navigational routing; nested routing; nested routing with path parameters; protected routing If not, head over to “Getting Started with React and JSX”. The <Route> component is the most important component in React router. by Emmanuel Yusufu Beginner’s Guide to React Router 4 React is a JavaScript library for building user interfaces. With the React paradigm, every piece of the UI is a component that manages its own self-contained state (data) and functions. React, like other front-end JavaScript frameworks, is useful for creating Single Page Applications (SPAs). These are web apps that do not need a full page

A guide to using React Router v6 in React apps, The Route component is perhaps the most important component in React Router to import ReactDOM from "react-dom"; import { BrowserRouter as Router, Route } added to each route component will cause React to recreate the component Sometimes you need to render whether the path matches the location or not. In� However, now that I am on v6.0.0 of both connected-react-router and react-redux I am noticing some strange things occurring with the ConnectedRouter component. First issue. As others have already stated, the ConnectedRouter seems to be rendering two times on initial page load. Second issue

React Router Frequently Faced Problems, No Account Required If you have experience working with routing in React apps, you may function App() { return <Router>{/* All routes are nested inside it */}</Router>; } Next, update the App function component with the following route. import { BrowserRouter as Router, Routes, Route, Link } from� ResourceUI component have 2 sections. the left section have links and right section have component that when links from the left is clicked, the right component should be rendered accordingly. Like I said above, the resource component is routed correctly but the <route/> inside the resource component is not rendering when <link/> is clicked.

Use matchPath to Match Nested Route Paths in Parent Routes with , Routing is one of the toughest thing to Implement in Single Page App In React If you click index(“/”) route link you will see routes URL is changed to it not look for change in URL so to overcome we gonna use withRouter from any nested components these won't render and surprisingly react router�

Comments
  • Totally agree so I ust added the relevant parts of the component code. Thanks for the working example, but I'm not sure it matches my needs because I would like to not have to rerender the header/footer when changing to next/prev portfolio items. Still, in the spirit of getting it to work I tried it without nesing, similar to what you did (but no /portfolio route and targeting the parent component instead of the Item detail). It still works as before, but I still have no luck getting Link to remount the component.
  • It might also be worth mentioning that this is a universal app. It's working fine when the pages are linked to from a non /portfolio route (like Home) but I can't seem to link between portfolio pages without a refresh. Live example is here: jasongallagher.org
  • did you figure this out? I'm hitting the same issue. Is it because components have their own "route"? what can cause them to trigger and update?
  • Not really, except the workaround below. Any state change should trigger an update but for some reason Link isn't always doing that. At some point I may switch to React Router Redux and see if that fixes it as I'm using Redux.
  • Note that componentWillReceiveProps is deprecated as of 16, though, and will be removed entirely in 17.