React Context API + withRouter - can we use them together?












1















I built a large application where a single button on the navbar opens a modal.



I'm keeping track of the modalOpen state using context API.



So, user clicks button on navbar. Modal Opens. Modal has container called QuoteCalculator.



QuoteCalculator looks as follows:



class QuoteCalculator extends React.Component {
static contextType = ModalContext;
// ...

onSubmit = () => {
// ...
this.context.toggleModal();
this.props.history.push('/quote');
// ..
};

render() {
//...
return(<Question {...props} next={this.onSubmit} />;)
}
}

export default withRouter(QuoteCalculator);


Now, everything works as expected. When the user submits, I go to the right route. I just see the following warning on the console




index.js:1446 Warning: withRouter(QuoteCalculator): Function
components do not support contextType.




I'm tempted to ignore the warning, but I don't think its a good idea.



I tried using Redirect alternatively. So something like



QuoteCalculator looks as follows:

class QuoteCalculator extends React.Component {
static contextType = ModalContext;
// ...

onSubmit = () => {
// ...
this.context.toggleModal();
this.setState({done: true});
// ..
};

render() {
let toDisplay;
if(this.state.done) {
toDisplay = <Redirect to="/quote"/>
} else {
toDipslay = <Question {...props} next={this.onSubmit} />;
}
return(<>{toDisplay}</>)
}
}

export default QuoteCalculator;


The problem with this approach is that I kept on getting the error




You tried to redirect to the same route you're currently on




Also, I'd rather not use this approach, just because then I'd have to undo the state done (otherwise when user clicks button again, done is true, and we'll just get redirected) ...



Any idea whats going on with withRouter and history.push?



Here's my app



class App extends Component {
render() {
return (
<Layout>
<Switch>
<Route path="/quote" component={Quote} />
<Route path="/pricing" component={Pricing} />
<Route path="/about" component={About} />
<Route path="/faq" component={FAQ} />
<Route path="/" exact component={Home} />
<Redirect to="/" />
</Switch>
</Layout>
);
}
}









share|improve this question



























    1















    I built a large application where a single button on the navbar opens a modal.



    I'm keeping track of the modalOpen state using context API.



    So, user clicks button on navbar. Modal Opens. Modal has container called QuoteCalculator.



    QuoteCalculator looks as follows:



    class QuoteCalculator extends React.Component {
    static contextType = ModalContext;
    // ...

    onSubmit = () => {
    // ...
    this.context.toggleModal();
    this.props.history.push('/quote');
    // ..
    };

    render() {
    //...
    return(<Question {...props} next={this.onSubmit} />;)
    }
    }

    export default withRouter(QuoteCalculator);


    Now, everything works as expected. When the user submits, I go to the right route. I just see the following warning on the console




    index.js:1446 Warning: withRouter(QuoteCalculator): Function
    components do not support contextType.




    I'm tempted to ignore the warning, but I don't think its a good idea.



    I tried using Redirect alternatively. So something like



    QuoteCalculator looks as follows:

    class QuoteCalculator extends React.Component {
    static contextType = ModalContext;
    // ...

    onSubmit = () => {
    // ...
    this.context.toggleModal();
    this.setState({done: true});
    // ..
    };

    render() {
    let toDisplay;
    if(this.state.done) {
    toDisplay = <Redirect to="/quote"/>
    } else {
    toDipslay = <Question {...props} next={this.onSubmit} />;
    }
    return(<>{toDisplay}</>)
    }
    }

    export default QuoteCalculator;


    The problem with this approach is that I kept on getting the error




    You tried to redirect to the same route you're currently on




    Also, I'd rather not use this approach, just because then I'd have to undo the state done (otherwise when user clicks button again, done is true, and we'll just get redirected) ...



    Any idea whats going on with withRouter and history.push?



    Here's my app



    class App extends Component {
    render() {
    return (
    <Layout>
    <Switch>
    <Route path="/quote" component={Quote} />
    <Route path="/pricing" component={Pricing} />
    <Route path="/about" component={About} />
    <Route path="/faq" component={FAQ} />
    <Route path="/" exact component={Home} />
    <Redirect to="/" />
    </Switch>
    </Layout>
    );
    }
    }









    share|improve this question

























      1












      1








      1








      I built a large application where a single button on the navbar opens a modal.



      I'm keeping track of the modalOpen state using context API.



      So, user clicks button on navbar. Modal Opens. Modal has container called QuoteCalculator.



      QuoteCalculator looks as follows:



      class QuoteCalculator extends React.Component {
      static contextType = ModalContext;
      // ...

      onSubmit = () => {
      // ...
      this.context.toggleModal();
      this.props.history.push('/quote');
      // ..
      };

      render() {
      //...
      return(<Question {...props} next={this.onSubmit} />;)
      }
      }

      export default withRouter(QuoteCalculator);


      Now, everything works as expected. When the user submits, I go to the right route. I just see the following warning on the console




      index.js:1446 Warning: withRouter(QuoteCalculator): Function
      components do not support contextType.




      I'm tempted to ignore the warning, but I don't think its a good idea.



      I tried using Redirect alternatively. So something like



      QuoteCalculator looks as follows:

      class QuoteCalculator extends React.Component {
      static contextType = ModalContext;
      // ...

      onSubmit = () => {
      // ...
      this.context.toggleModal();
      this.setState({done: true});
      // ..
      };

      render() {
      let toDisplay;
      if(this.state.done) {
      toDisplay = <Redirect to="/quote"/>
      } else {
      toDipslay = <Question {...props} next={this.onSubmit} />;
      }
      return(<>{toDisplay}</>)
      }
      }

      export default QuoteCalculator;


      The problem with this approach is that I kept on getting the error




      You tried to redirect to the same route you're currently on




      Also, I'd rather not use this approach, just because then I'd have to undo the state done (otherwise when user clicks button again, done is true, and we'll just get redirected) ...



      Any idea whats going on with withRouter and history.push?



      Here's my app



      class App extends Component {
      render() {
      return (
      <Layout>
      <Switch>
      <Route path="/quote" component={Quote} />
      <Route path="/pricing" component={Pricing} />
      <Route path="/about" component={About} />
      <Route path="/faq" component={FAQ} />
      <Route path="/" exact component={Home} />
      <Redirect to="/" />
      </Switch>
      </Layout>
      );
      }
      }









      share|improve this question














      I built a large application where a single button on the navbar opens a modal.



      I'm keeping track of the modalOpen state using context API.



      So, user clicks button on navbar. Modal Opens. Modal has container called QuoteCalculator.



      QuoteCalculator looks as follows:



      class QuoteCalculator extends React.Component {
      static contextType = ModalContext;
      // ...

      onSubmit = () => {
      // ...
      this.context.toggleModal();
      this.props.history.push('/quote');
      // ..
      };

      render() {
      //...
      return(<Question {...props} next={this.onSubmit} />;)
      }
      }

      export default withRouter(QuoteCalculator);


      Now, everything works as expected. When the user submits, I go to the right route. I just see the following warning on the console




      index.js:1446 Warning: withRouter(QuoteCalculator): Function
      components do not support contextType.




      I'm tempted to ignore the warning, but I don't think its a good idea.



      I tried using Redirect alternatively. So something like



      QuoteCalculator looks as follows:

      class QuoteCalculator extends React.Component {
      static contextType = ModalContext;
      // ...

      onSubmit = () => {
      // ...
      this.context.toggleModal();
      this.setState({done: true});
      // ..
      };

      render() {
      let toDisplay;
      if(this.state.done) {
      toDisplay = <Redirect to="/quote"/>
      } else {
      toDipslay = <Question {...props} next={this.onSubmit} />;
      }
      return(<>{toDisplay}</>)
      }
      }

      export default QuoteCalculator;


      The problem with this approach is that I kept on getting the error




      You tried to redirect to the same route you're currently on




      Also, I'd rather not use this approach, just because then I'd have to undo the state done (otherwise when user clicks button again, done is true, and we'll just get redirected) ...



      Any idea whats going on with withRouter and history.push?



      Here's my app



      class App extends Component {
      render() {
      return (
      <Layout>
      <Switch>
      <Route path="/quote" component={Quote} />
      <Route path="/pricing" component={Pricing} />
      <Route path="/about" component={About} />
      <Route path="/faq" component={FAQ} />
      <Route path="/" exact component={Home} />
      <Redirect to="/" />
      </Switch>
      </Layout>
      );
      }
      }






      reactjs react-router react-context






      share|improve this question













      share|improve this question











      share|improve this question




      share|improve this question










      asked Dec 29 '18 at 22:10









      AsoolAsool

      5691621




      5691621
























          1 Answer
          1






          active

          oldest

          votes


















          2














          Source of the warning



          Unlike most higher order components, withRouter is wrapping the component you pass inside a functional component instead of a class component. But it's still calling hoistStatics, which is taking your contextType static and moving it to the function component returned by withRouter. That should usually be fine, but you've found an instance where it's not. You can check the repo code for more details, but it's short so I'm just going to drop the relevant lines here for you:



          function withRouter(Component) {
          // this is a functional component
          const C = props => {
          const { wrappedComponentRef, ...remainingProps } = props;

          return (
          <Route
          children={routeComponentProps => (
          <Component
          {...remainingProps}
          {...routeComponentProps}
          ref={wrappedComponentRef}
          />
          )}
          />
          );
          };

          // ...
          // hoistStatics moves statics from Component to C
          return hoistStatics(C, Component);
          }


          It really shouldn't negatively impact anything. Your context will still work and will just be ignored on the wrapping component returned from withRouter. However, it's not difficult to alter things to remove that problem.



          Possible Solutions



          Simplest



          Since all you need in your modal is history.push, you could just pass that as a prop from the modal's parent component. Given the setup you described, I'm guessing the modal is included in one place in the app, fairly high up in the component tree. If the component that includes your modal is already a Route component, then it has access to history and can just pass push along to the modal. If it's not, then wrap the parent component in withRouter to get access to the router props.



          Not bad



          You could also make your modal component a simple wrapper around your modal content/functionality, using the ModalContext.Consumer component to pass the needed context down as props instead of using contextType.



          const Modal = () => (
          <ModalContext.Consumer>
          {value => <ModalContent {...value} />}
          </ModalContext.Consumer>
          )

          class ModalContent extends React.Component {
          onSubmit = () => {
          // ...
          this.props.toggleModal()
          this.props.history.push('/quote')
          // ..
          }

          // ...
          }





          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%2f53973720%2freact-context-api-withrouter-can-we-use-them-together%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









            2














            Source of the warning



            Unlike most higher order components, withRouter is wrapping the component you pass inside a functional component instead of a class component. But it's still calling hoistStatics, which is taking your contextType static and moving it to the function component returned by withRouter. That should usually be fine, but you've found an instance where it's not. You can check the repo code for more details, but it's short so I'm just going to drop the relevant lines here for you:



            function withRouter(Component) {
            // this is a functional component
            const C = props => {
            const { wrappedComponentRef, ...remainingProps } = props;

            return (
            <Route
            children={routeComponentProps => (
            <Component
            {...remainingProps}
            {...routeComponentProps}
            ref={wrappedComponentRef}
            />
            )}
            />
            );
            };

            // ...
            // hoistStatics moves statics from Component to C
            return hoistStatics(C, Component);
            }


            It really shouldn't negatively impact anything. Your context will still work and will just be ignored on the wrapping component returned from withRouter. However, it's not difficult to alter things to remove that problem.



            Possible Solutions



            Simplest



            Since all you need in your modal is history.push, you could just pass that as a prop from the modal's parent component. Given the setup you described, I'm guessing the modal is included in one place in the app, fairly high up in the component tree. If the component that includes your modal is already a Route component, then it has access to history and can just pass push along to the modal. If it's not, then wrap the parent component in withRouter to get access to the router props.



            Not bad



            You could also make your modal component a simple wrapper around your modal content/functionality, using the ModalContext.Consumer component to pass the needed context down as props instead of using contextType.



            const Modal = () => (
            <ModalContext.Consumer>
            {value => <ModalContent {...value} />}
            </ModalContext.Consumer>
            )

            class ModalContent extends React.Component {
            onSubmit = () => {
            // ...
            this.props.toggleModal()
            this.props.history.push('/quote')
            // ..
            }

            // ...
            }





            share|improve this answer




























              2














              Source of the warning



              Unlike most higher order components, withRouter is wrapping the component you pass inside a functional component instead of a class component. But it's still calling hoistStatics, which is taking your contextType static and moving it to the function component returned by withRouter. That should usually be fine, but you've found an instance where it's not. You can check the repo code for more details, but it's short so I'm just going to drop the relevant lines here for you:



              function withRouter(Component) {
              // this is a functional component
              const C = props => {
              const { wrappedComponentRef, ...remainingProps } = props;

              return (
              <Route
              children={routeComponentProps => (
              <Component
              {...remainingProps}
              {...routeComponentProps}
              ref={wrappedComponentRef}
              />
              )}
              />
              );
              };

              // ...
              // hoistStatics moves statics from Component to C
              return hoistStatics(C, Component);
              }


              It really shouldn't negatively impact anything. Your context will still work and will just be ignored on the wrapping component returned from withRouter. However, it's not difficult to alter things to remove that problem.



              Possible Solutions



              Simplest



              Since all you need in your modal is history.push, you could just pass that as a prop from the modal's parent component. Given the setup you described, I'm guessing the modal is included in one place in the app, fairly high up in the component tree. If the component that includes your modal is already a Route component, then it has access to history and can just pass push along to the modal. If it's not, then wrap the parent component in withRouter to get access to the router props.



              Not bad



              You could also make your modal component a simple wrapper around your modal content/functionality, using the ModalContext.Consumer component to pass the needed context down as props instead of using contextType.



              const Modal = () => (
              <ModalContext.Consumer>
              {value => <ModalContent {...value} />}
              </ModalContext.Consumer>
              )

              class ModalContent extends React.Component {
              onSubmit = () => {
              // ...
              this.props.toggleModal()
              this.props.history.push('/quote')
              // ..
              }

              // ...
              }





              share|improve this answer


























                2












                2








                2







                Source of the warning



                Unlike most higher order components, withRouter is wrapping the component you pass inside a functional component instead of a class component. But it's still calling hoistStatics, which is taking your contextType static and moving it to the function component returned by withRouter. That should usually be fine, but you've found an instance where it's not. You can check the repo code for more details, but it's short so I'm just going to drop the relevant lines here for you:



                function withRouter(Component) {
                // this is a functional component
                const C = props => {
                const { wrappedComponentRef, ...remainingProps } = props;

                return (
                <Route
                children={routeComponentProps => (
                <Component
                {...remainingProps}
                {...routeComponentProps}
                ref={wrappedComponentRef}
                />
                )}
                />
                );
                };

                // ...
                // hoistStatics moves statics from Component to C
                return hoistStatics(C, Component);
                }


                It really shouldn't negatively impact anything. Your context will still work and will just be ignored on the wrapping component returned from withRouter. However, it's not difficult to alter things to remove that problem.



                Possible Solutions



                Simplest



                Since all you need in your modal is history.push, you could just pass that as a prop from the modal's parent component. Given the setup you described, I'm guessing the modal is included in one place in the app, fairly high up in the component tree. If the component that includes your modal is already a Route component, then it has access to history and can just pass push along to the modal. If it's not, then wrap the parent component in withRouter to get access to the router props.



                Not bad



                You could also make your modal component a simple wrapper around your modal content/functionality, using the ModalContext.Consumer component to pass the needed context down as props instead of using contextType.



                const Modal = () => (
                <ModalContext.Consumer>
                {value => <ModalContent {...value} />}
                </ModalContext.Consumer>
                )

                class ModalContent extends React.Component {
                onSubmit = () => {
                // ...
                this.props.toggleModal()
                this.props.history.push('/quote')
                // ..
                }

                // ...
                }





                share|improve this answer













                Source of the warning



                Unlike most higher order components, withRouter is wrapping the component you pass inside a functional component instead of a class component. But it's still calling hoistStatics, which is taking your contextType static and moving it to the function component returned by withRouter. That should usually be fine, but you've found an instance where it's not. You can check the repo code for more details, but it's short so I'm just going to drop the relevant lines here for you:



                function withRouter(Component) {
                // this is a functional component
                const C = props => {
                const { wrappedComponentRef, ...remainingProps } = props;

                return (
                <Route
                children={routeComponentProps => (
                <Component
                {...remainingProps}
                {...routeComponentProps}
                ref={wrappedComponentRef}
                />
                )}
                />
                );
                };

                // ...
                // hoistStatics moves statics from Component to C
                return hoistStatics(C, Component);
                }


                It really shouldn't negatively impact anything. Your context will still work and will just be ignored on the wrapping component returned from withRouter. However, it's not difficult to alter things to remove that problem.



                Possible Solutions



                Simplest



                Since all you need in your modal is history.push, you could just pass that as a prop from the modal's parent component. Given the setup you described, I'm guessing the modal is included in one place in the app, fairly high up in the component tree. If the component that includes your modal is already a Route component, then it has access to history and can just pass push along to the modal. If it's not, then wrap the parent component in withRouter to get access to the router props.



                Not bad



                You could also make your modal component a simple wrapper around your modal content/functionality, using the ModalContext.Consumer component to pass the needed context down as props instead of using contextType.



                const Modal = () => (
                <ModalContext.Consumer>
                {value => <ModalContent {...value} />}
                </ModalContext.Consumer>
                )

                class ModalContent extends React.Component {
                onSubmit = () => {
                // ...
                this.props.toggleModal()
                this.props.history.push('/quote')
                // ..
                }

                // ...
                }






                share|improve this answer












                share|improve this answer



                share|improve this answer










                answered Dec 31 '18 at 18:48









                shadymosesshadymoses

                1,4861011




                1,4861011






























                    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%2f53973720%2freact-context-api-withrouter-can-we-use-them-together%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

                    Angular Downloading a file using contenturl with Basic Authentication

                    Olmecas

                    Can't read property showImagePicker of undefined in react native iOS