React Parent Component completely reconstructed when using Redirect












1















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!










share|improve this question





























    1















    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!










    share|improve this question



























      1












      1








      1








      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!










      share|improve this question
















      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!







      javascript reactjs react-router react-router-dom nested-routes






      share|improve this question















      share|improve this question













      share|improve this question




      share|improve this question








      edited Nov 27 '18 at 12:46







      cherries

















      asked Nov 27 '18 at 11:58









      cherriescherries

      135




      135
























          1 Answer
          1






          active

          oldest

          votes


















          0














          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>
          );
          }
          }





          share|improve this answer























            Your Answer






            StackExchange.ifUsing("editor", function () {
            StackExchange.using("externalEditor", function () {
            StackExchange.using("snippets", function () {
            StackExchange.snippets.init();
            });
            });
            }, "code-snippets");

            StackExchange.ready(function() {
            var channelOptions = {
            tags: "".split(" "),
            id: "1"
            };
            initTagRenderer("".split(" "), "".split(" "), channelOptions);

            StackExchange.using("externalEditor", function() {
            // Have to fire editor after snippets, if snippets enabled
            if (StackExchange.settings.snippets.snippetsEnabled) {
            StackExchange.using("snippets", function() {
            createEditor();
            });
            }
            else {
            createEditor();
            }
            });

            function createEditor() {
            StackExchange.prepareEditor({
            heartbeatType: 'answer',
            autoActivateHeartbeat: false,
            convertImagesToLinks: true,
            noModals: true,
            showLowRepImageUploadWarning: true,
            reputationToPostImages: 10,
            bindNavPrevention: true,
            postfix: "",
            imageUploader: {
            brandingHtml: "Powered by u003ca class="icon-imgur-white" href="https://imgur.com/"u003eu003c/au003e",
            contentPolicyHtml: "User contributions licensed under u003ca href="https://creativecommons.org/licenses/by-sa/3.0/"u003ecc by-sa 3.0 with attribution requiredu003c/au003e u003ca href="https://stackoverflow.com/legal/content-policy"u003e(content policy)u003c/au003e",
            allowUrls: true
            },
            onDemand: true,
            discardSelector: ".discard-answer"
            ,immediatelyShowMarkdownHelp:true
            });


            }
            });














            draft saved

            draft discarded


















            StackExchange.ready(
            function () {
            StackExchange.openid.initPostLogin('.new-post-login', 'https%3a%2f%2fstackoverflow.com%2fquestions%2f53499179%2freact-parent-component-completely-reconstructed-when-using-redirect%23new-answer', 'question_page');
            }
            );

            Post as a guest















            Required, but never shown

























            1 Answer
            1






            active

            oldest

            votes








            1 Answer
            1






            active

            oldest

            votes









            active

            oldest

            votes






            active

            oldest

            votes









            0














            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>
            );
            }
            }





            share|improve this answer




























              0














              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>
              );
              }
              }





              share|improve this answer


























                0












                0








                0







                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>
                );
                }
                }





                share|improve this answer













                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>
                );
                }
                }






                share|improve this answer












                share|improve this answer



                share|improve this answer










                answered Nov 27 '18 at 13:21









                cherriescherries

                135




                135
































                    draft saved

                    draft discarded




















































                    Thanks for contributing an answer to Stack Overflow!


                    • Please be sure to answer the question. Provide details and share your research!

                    But avoid



                    • Asking for help, clarification, or responding to other answers.

                    • Making statements based on opinion; back them up with references or personal experience.


                    To learn more, see our tips on writing great answers.




                    draft saved


                    draft discarded














                    StackExchange.ready(
                    function () {
                    StackExchange.openid.initPostLogin('.new-post-login', 'https%3a%2f%2fstackoverflow.com%2fquestions%2f53499179%2freact-parent-component-completely-reconstructed-when-using-redirect%23new-answer', 'question_page');
                    }
                    );

                    Post as a guest















                    Required, but never shown





















































                    Required, but never shown














                    Required, but never shown












                    Required, but never shown







                    Required, but never shown

































                    Required, but never shown














                    Required, but never shown












                    Required, but never shown







                    Required, but never shown







                    Popular posts from this blog

                    A CLEAN and SIMPLE way to add appendices to Table of Contents and bookmarks

                    Calculate evaluation metrics using cross_val_predict sklearn

                    Insert data from modal to MySQL (multiple modal on website)