I'm creating my first app with react-router and was having some trouble with an invalid hook error. I did manage to fix the error, but I don't really understand why this fixed it. So, in my App component I had a Switch with the following Route:
<Route
path="/signin"
exact
render={SignIn}
/>
Now lets suppose the SignIn component looked like this:
import React, { useState } from 'react';
const SignIn = () => {
const [test, setTest] = useState('test');
return (
<main>
{test}
</main>
)
}
export default SignIn;
With this set-up I kept getting an invalid hooks error, which only went away when I changed the Route in the App component to this:
<Route
path="/signin"
exact
render={(props) => <SignIn {...props}/>}
/>
Now this had solved the problem and the invalid hook problem went away, but why? I am not actually using any props in the SingIn component, at least not at the moment. Which of the React Hook Rules am I breaking here?
Since SignIn is a component, you can do it like this:
<Route
path="/signin"
exact
component={SignIn}
/>
The render syntax is when you want to pass some additional props
Related
We are looking to add custom logging to our react application, and would like to log each time a user changes routes. To handle this, we are creating a wrapper component , and this is what we currently have:
import React, { useEffect } from 'react';
import { Route } from 'react-router-dom';
function LoggerRoute(isExact, path, element) {
useEffect(() => {
// send route-change log event to our mongodb collection
}, []);
// And return Route
return (
isExact
? <Route exact path={path} element={element} />
: <Route exact path={path} element={element} />
);
}
export default LoggerRoute;
...and in our App.js file, we have changed the routes as such:
// remove this // <Route exact path='/tools/team-scatter' element={<TeamScatterApp />} />
<LoggerRoute isExact={true} path='/tools/team-scatter' element={<TeamScatterApp />} />
However, this throws the error Uncaught Error: [LoggerRoute] is not a <Route> component. All component children of <Routes> must be a <Route> or <React.Fragment>.
Additionally, it feels off passing props to a route, if possible we would prefer
<LoggerRoute exact path='/tools/team-scatter' element={<TeamScatterApp />} />
as the ideal way to call our LoggerRoute. We like the idea of a wrapper component, this way we don’t have to add logging into every component that our app routes to. However, I’m not sure if this wrapper component approach is possible if can only accept a component. How can we modify our LoggerRoute component to work / be better?
In react-router-dom#6 only Route and React.Fragment are valid children of the Routes component. Create either a wrapper component or a layout route component to handle listening for route path changes. Since you presumably want to do this for more than one route at-a-time I suggest the layout route method, but you can create a component that handles either.
Example:
import { Outlet, useLocation } from 'react-router-dom';
const RouteListenerLayout = ({ children }) => {
const { pathname } = useLocation();
useEffect(() => {
// send route-change log event to our mongodb collection
}, [pathname]);
return children ?? <Outlet />;
};
The children prop is used in the cases where you want to wrap individual routed components (i.e. the element prop) or an entire component, (*i.e. App) that is rendering a set of Routes. The Outlet component is used in the case where you want to conditionally include a subset of routes within a Routes component (i.e. some nested Route components).
Wrap the routes you want to listen to route changes for.
Examples:
<Routes>
<Route element={<RouteListenerLayout />}>
<Route path="path1" element={<SomeComponent />} />
<Route path="someOtherPath" element={<SomeOtherComponent />} />
... wrapped routes components with listener
</Route>
... routes w/o listener
</Routes>
or
<Routes>
<Route
path="/"
element={(
<RouteListenerLayout>
<SomeComponent />
</RouteListenerLayout>
)}
/>
</Routes>
or
<RouteListenerLayout>
<App />
</RouteListenerLayout>
These all assume the router is rendered higher in the ReactTree than RouteListenerLayout so the useLocation hook works as expected.
In v6, <Route> is a lot more strict than it was in v5. Instead of building wrappers for , it may be used only inside other <Routes> or <Route> elements. If you try to wrap a <Route> in another component it will never render.
What you should be doing instead is adding a wrapper component and leveraging it in the element prop on the route.
function App() {
return (
<Routes>
<Route path="/public" element={<PublicPage />} />
<Route
path="/route"
element={
<AddLogging>
<YourPage/>
</AddLogging>
}
/>
</Routes>
);
}
Edit: here is an example wrapper component based on your needs:
function AddLogging({children}) {
useEffect(() => {
// send route-change log event to our mongodb collection
// can use useLocation hook to get route to log
}, []);
return children;
}
I am trying to use hooks inside the functional component. Just defining the hook itself I am getting an error.
import React, { useRef } from "react";
function CreateTemplate() {
const editorRef = useRef();
return (<div>something</div>)
}
And this file is called on click of the previous component, where history is used. Adding it to the
<button
onClick={() => {
history.push({
pathname: "/create-template",
});
}}
>
CONFIRM TEMPLATE
</button>
And Route is defined below.
<Switch>
<AuthRoute path='/dashboard' render={Dashboard} type='private' />
<AuthRoute path='/templates' render={Templates} type='private' />
<AuthRoute
path='/create-template'
render={CreateTemplate}
type='private'
/>
<Route path='/' render={Dashboard} />
</Switch>
Below is the react and react-dom version
"react": "^17.0.2",
"react-dom": "^17.0.2"
I am not sure why useRef hook or any hook throwing above error. Tried almost all possible solution.
You are likely not rendering CreateTemplate correctly in the route, a render prop typically takes a function value. Depending on your AuthRoute implementation I believe you can do one of the following:
Use the component prop:
<AuthRoute
path='/create-template'
component={CreateTemplate}
type='private'
/>
Keep the render prop and render an anonymous component:
<AuthRoute
path='/create-template'
render={routeProps => <CreateTemplate {...routeProps} />}
type='private'
/>
If neither of these are the case then surely we'll need to see your AuthRoute component implementation to see how it's rendering.
I am working on existing code base for a react application. I am new to React and the developer who wrote this left.
In my current code base "request-promise-native" is pass from one component to another component using props. I don't get this. Why not just import it again in every component?
App.tsx
import rp from 'request-promise-native';
const App: React.FC = () => {
return (
<Router>
<Route path="/" render={(props) => <Main {...props} rp={rp} />}></Route>
</Router>
)
};
After reading the documentation of https://www.npmjs.com/package/request-promise it appears this is completely pointless and you are correct about imports.
So I have a single Route /reminder, where I do not want my Navigation component, but I want the Navigation component to render on every other Route and I'm not sure how to go about this?
This is my App.js where I am using react-router-dom for my routing.
import React from "react";
import { BrowserRouter as Router, Route, Switch } from "react-router-dom";
import Navigation from "./components/navigation/Navigation";
import Reminder from "./components/reminder/Reminder ";
const App = () => {
return (
<>
<Switch>
<Route path="/reminder" component={Reminder} exact />
</Switch>
<Navigation />
<MainContent>
<Router>
<Route path={"/"} component={Dashboard} exact />
</Router>
</MainContent>
</>
);
}
I had a look around for similar issues, but it's mostly for authenticated pages, this isn't for an authenticated page.
I don't want to go down the route of having to add the Navigation component into each of my 21 current routes, it seems like there should a better method?
With my current code above, it renders the Reminder component but still renders the Navigation component.
You can access the match data using a custom hook from react-router. All you need to do is
import { useRouteMatch } from "react-router-dom";
function App() {
let match = useRouteMatch("/reminder");
// Do whatever you want with the match...
return (
<>
<Switch>
<Route path="/reminder" component={Reminder} exact />
</Switch>
{!match && <Navigation />} <---Conditional rendering
<MainContent>
<Router>
<Route path={"/"} component={Dashboard} exact />
</Router>
</MainContent>
</>
);
}
Also, while I didn't change this in your example, it's not generally a good idea to have a route outside of the router component. I'm actually a little surprised react-router-dom didn't throw an error for you.
I've created a repository with a basic example that triggers this error in case it helps:
loadable-components-ssr
I'm trying to use Loadable Components in a SSR set up with react-router-dom 4.3.1, loadable-component 5.6.0 and react-dom 16.8.1
Here is a component example to which I'm trying to apply loadable-component:
import React from "react";
const About = () => <h2>About</h2>;
export default About;
This is imported in the App component like this:
import loadable from "#loadable/component";
...
const About = loadable(() => import("./About"));
And passed as a prop to Route in the same App component:
<Route path="/about/" component={About} />
But I keep getting the following warning in the Developer Tools console:
Warning: Failed prop type: Invalid prop component of type object supplied to Route, expected function
If I use an alternative syntax as suggested in the first answer:
<Route path="/about/" component={props => <About {...props} />} />
The warning disappears, but the route to /about still gives an error when the link is clicked:
Uncaught Error: Loading chunk About failed.
(missing: http://localhost:3000/about/About.bundle.js)
at HTMLScriptElement.onScriptComplete (VM1805 app.bundle.js:114)
I followed the documentation about setting up loadable-components in SSR, so I've set up the client, the server and also the babel plugin as indicated.
Any idea what's wrong here?
This is a known issue of react router.
I think that you could code the route like this:
<Route path="/about/" component={props => <About {...props} />} />
Be careful with this implementation, because you could have some buggy behaviours with the re-renders.
this is your "About" component:
import React from "react";
const About = () => <h2>About</h2>;
export default About;
you are not returning jsx. thats why you are getting error.
this is how you should have returned jsx.
const About = () => (<h2>About</h2>);
cheers!
why don't you use react.lazy? It is the official component.
const About = React.lazy(() => import('./About')
<Route exact path="/about" component={props => <About {...props} />} />