React Parent Component completely reconstructed when using Redirect
Asked Answered
B

1

7

I am trying to utilize nested routes in order to maintain the appearance of a parent component on screen, and as the user navigates left and right in a tab structure, the content in that tabbed interface updates. I do not want the parent component to remount or even re-render, only it's children to change. This is a completely keyboard-based navigation system, so we are not using Links or click events, just hitting left/right/up/down/enter on the keyboard.

Unfortunately, I cannot share my exact code for privacy reasons, but here is the general code structure (obviously not compilable, just to get the gist of what our architecture is like).

in App.js

class App extends Component {
  render() {
    return (
      <div className="App">
        <Switch>
          <Route
            path="/"
            exact
            match={ true }
            component={ () => <MainMenu/> }
          />
          <Route
            path="/category/:uniqueID"
            exact
            component={ () => <CategoryComponent childArray=[child1, child2, child3] /> }
          />
      />
        </Switch>
      </div>
    );
  }
}

in CategoryComponent.js

class CategoryComponent extends Component {
  render() {
    var childRoutes;
    this.props.childArray.forEach(child => {
      childRoutes.push(
        <Route
          key=child.id
          path={ `${this.props.match.path}/${child.path}` }
          component={ () => <ChildComponent/> }
        />
      );
    });

    return (
      <div className="CategoryComponent">
        ... // a bunch of UI stuff goes here, including the navigation for the child components
        <Switch>
          { childRoutes }
        </Switch>
      </div>
    );
  }
}

and finally, in the ChildComponent.js

class ChildComponent extends Component {
  shouldComponentUpdate(nextProps) {
    // because this navigation is done exclusively by keyboard,
    // each child has a property of selected or not that it gets from its parent,
    // so only the currently selected one should actually be doing the redirect
    if (!this.props.isSelected && nextProps.isSelected) {
      this.redirect = true;
    }
  }
  render() {
    var redirect;
    if (this.redirect) {
      redirect = 
        <Redirect
          to={ `${this.props.match.path}/${this.props.thisChildPath}` }
        />;
    }

    return (
      <div className="ChildComponent">
        { redirect }
      </div>
    );
  }
}

Hopefully all of the above made sense, it's as simple as I think I can make it from our crazy complex application. Basically:

  • we have an app with a route that utilizes a unique ID, ie. myApp.com/category/1234
  • inside of this category, we have some tabs to navigate to, like blue, red, yellow, and for each one, we want to nest the route inside of the above and wind up with something like myApp.com/category/1234/blue, which will update only a part of the screen

The problem I am having is that it seems no matter where I place the redirects, if I use exact or (non-exact paths), or if I use push as true in the Redirect, the parent component ALWAYS remounts. I end up with an entirely new parent component, which will wipe out certain elements saved in local state. The App never remounts, but the Parent Component does. I only want the Child Components to remount, the parent should stay exactly as is. Originally the Category component was even a Higher Order Component, and we switched this to just one normal component class, but with or without conditionally rendering specific cases, didn't seem to make any different either.

Also, I have been playing around with this codesandbox, and adapted the original code to more closely match my project and utilize both links and redirects, and the parent app component never seemed to remount. So...it SEEMS like it's possible, I'm just not sure what I'm doing wrong.

Any help would be greatly appreciated :) thanks!

Bookstand answered 27/11, 2018 at 11:58 Comment(0)
B
1

This helped me out a ton: https://github.com/ReactTraining/react-router/issues/6122

I realized that our App's Switch Routing was using component, which was causing the reconstruction every time. By switching those all to render, the problem was solved :D

So App.js became this:

class App extends Component {
  render() {
    return (
      <div className="App">
        <Switch>
          <Route
           path="/"
            exact
            match={ true }
            render={ () => <MainMenu/> }
          />
          <Route
            path="/category/:uniqueID"
            exact
            render={ () => <CategoryComponent childArray=[child1, child2, child3] /> }
          />
      />
        </Switch>
      </div>
    );
  }
}
Bookstand answered 27/11, 2018 at 13:21 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.