I am having issue with React router v6 while using Elements of stripe

Viewed 53

Uncaught Error: [Elements] is not a <Route> component. All component children of <Routes> must be a <Route> or <React.Fragment>

I even tried to use Fragment

I am also using the protected route.

    const App = () => {
  const { isAuthenticated, user } = useSelector((state) => state.user);

  const [stripeApiKey, setStripeApiKey] = useState();

  async function getStripeApiKey() {
    const { data } = await axios.get("/api/v1/stripeapikey");
    setStripeApiKey(data.stripeApiKey);
  }

  useEffect(() => {
    WebFont.load({
      google: {
        families: ["Roboto", "Droid Sans", "Chilanka"],
      },
    });
    store.dispatch(loadUser());
    getStripeApiKey();
  }, []);

  return (
    <div className="App">
      <BrowserRouter>
        <Header />
        <Routes>
          <Route path="/about" element={<About />} exact />
         
          <Route element={<ProtectedRoute />}>
            <Route path="/account" element={<Profile />} exact />
  
            {/* {stripeApiKey && (
              <Elements stripe={loadStripe(stripeApiKey)}>
                <Route exact path="/process/payment" element={<Payment />} />
              </Elements>
            )} */}
          </Route>
          <Route path="/password/forgot" element={<ForgotPassword />} exact />
          <Route
            path="/password/reset/:token"
            element={<ResetPassword />}
            exact
          />
          <Route path="/cart" element={<Cart />} exact />
        </Routes>

        <Footer />
      </BrowserRouter>
    </div>
  );
};
1 Answers

Issue

In react-router-dom@6 the Route component can only be rendered by the Routes component, or in the case of nested routes, another Route component. The Elements component is neither of these so it can't be rendered directly by a Route or Routes component. It appears that Elements is some sort of Stripe "context provider".

Solution

The solution here is to either wrap Payment directly with the Elements component, or render it in a layout route that renders an Outlet for the nested routes to render their element content into.

Wrapper Example

Using a wrapper component is useful if you only need to wrap a single routed component.

{stripeApiKey && (
  <Route
    path="/process/payment"
    element={(
      <Elements stripe={loadStripe(stripeApiKey)}>
        <Payment />
      </Elements>
    )}
  />
)}

Layout Example

Layout routes are useful if you need to wrap several routes.

import { Outlet } from 'react-router-dom';

const ElementsLayout = ({ stripe }) => (
  <Elements stripe={stripe}>
    <Outlet />
  </Elements>
);

export default ElementsLayout;

...

import ElementsLayout from '../path/to/ElementsLayout';

const App = () => {
  ...

  const [stripeApiKey, setStripeApiKey] = useState();

  ...

  return (
    <div className="App">
      <BrowserRouter>
        <Header />
        <Routes>
          <Route path="/about" element={<About />} />
          <Route element={<ProtectedRoute />}>
            <Route path="/account" element={<Profile />} />
            {stripeApiKey && (
              <Route
                element={<ElementsLayout stripe={loadStripe(stripeApiKey)} />}
              >
                <Route path="/process/payment" element={<Payment />} />
              </Route>
            )}
          </Route>
          <Route path="/password/forgot" element={<ForgotPassword />} />
          <Route
            path="/password/reset/:token"
            element={<ResetPassword />}
          />
          <Route path="/cart" element={<Cart />} />
        </Routes>
        <Footer />
      </BrowserRouter>
    </div>
  );
};
Related