So I am currently doing a Web Dev boot camp, and we are busy with a project where we make an API for an application where you can browse movies, select your favourite, make a profile etc.
The current task, has us implementing React Routers to the endpoints.
I followed the code as per the exercise but my app would only show my navbar and background colour. I went into the dev tools, and got this error.
utils.ts:767 Matched leaf route at location "/register" does not have an element. This means it will render an <Outlet /> with a null value by default resulting in an "empty" page.
OR
Matched leaf route at location "/" does not have an element. This means it will render an <Outlet /> with a null value by default resulting in an "empty" page.
I looked online for answers, but every answer refers to changing "component" to "element". The problem is, the code that we were taught, doesn't even have that included.
Just some of the code:
render() {
const { movies, user, favouriteMovies } = this.state;
console.log(favouriteMovies);
return (
<Router>
<NavBar user={user} />
<Row className="main-view justify-content-md-center">
<Routes>
<Route
exact
path="/"
render={() => {
/* If there is no user, the LoginView is rendered. If there is a user logged in,
the user details are passed as a prop to the LoginView */
if (!user)
return (
<Col>
<LoginView
movies={movies}
onLoggedIn={(user) => this.onLoggedIn(user)}
/>
</Col>
);
// Before the movies have been loaded
if (movies.length === 0) return <div className="main-view" />;
return movies.map((m) => (
<Col md={6} lg={3} key={m._id} className="movie-card">
<MovieCard movie={m} />
</Col>
));
}}
/>
<Route
path="/register"
render={() => {
if (user) return <Redirect to="/" />;
return (
<Col>
<RegistrationView />
</Col>
);
}}
/>
I then asked on Slack where the other students chat. There was someone with the same issue and they said they installed an older version and it worked.
npm install react-router-dom#5.3.0
I did that, but now my navbar doesn't show, only the background colour.
I also have a different error.
react-dom.development.js:86 Warning: ReactDOM.render is no longer supported in React 18. Use createRoot instead. Until you switch to the new API, your app will behave as if it's running React 17. Learn more: https://reactjs.org/link/switch-to-createroot
printWarning # react-dom.development.js:86
main-view.jsx:125 Array(0)
react-jsx-runtime.development.js:87 Warning: React.jsx: type is invalid -- expected a string (for built-in components) or a class/function (for composite components) but got: undefined. You likely forgot to export your component from the file it's defined in, or you might have mixed up default and named imports.
Check the render method of `MainView`.
at MainView (http://localhost:1234/index.6701a6e1.js:25282:9)
at MyFlixApplication (http://localhost:1234/index.6701a6e1.js:955:1)
printWarning # react-jsx-runtime.development.js:87
react-dom.development.js:28439 Uncaught Error: Element type is invalid: expected a string (for built-in components) or a class/function (for composite components) but got: undefined. You likely forgot to export your component from the file it's defined in, or you might have mixed up default and named imports.
Check the render method of `MainView`.
at createFiberFromTypeAndProps (react-dom.development.js:28439:17)
at createFiberFromElement (react-dom.development.js:28465:15)
at reconcileSingleElement (react-dom.development.js:15750:23)
at reconcileChildFibers (react-dom.development.js:15808:35)
at reconcileChildren (react-dom.development.js:19167:28)
at updateHostComponent (react-dom.development.js:19924:3)
at beginWork (react-dom.development.js:21618:14)
at HTMLUnknownElement.callCallback (react-dom.development.js:4164:14)
at Object.invokeGuardedCallbackDev (react-dom.development.js:4213:16)
at invokeGuardedCallback (react-dom.development.js:4277:31)
react-dom.development.js:18687 The above error occurred in the <div> component:
at div
at http://localhost:1234/index.6701a6e1.js:43098:48
at Router2 (http://localhost:1234/index.6701a6e1.js:29157:34)
at BrowserRouter2 (http://localhost:1234/index.6701a6e1.js:28837:39)
at MainView (http://localhost:1234/index.6701a6e1.js:25282:9)
at MyFlixApplication (http://localhost:1234/index.6701a6e1.js:955:1)
Consider adding an error boundary to your tree to customize error handling behavior.
Visit https://reactjs.org/link/error-boundaries to learn more about error boundaries.
My tutor isn't able to find what the problem is.
I have really been struggling with this and would really appreciate any assistance.
The link to my repo:
https://github.com/Claudiaj501/myFlix-client/tree/Router
React Router v6 makes heavy use of React hooks, so you'll need to be on React 16.8+. The React Router Docs and Upgrade Guide will show you the differences between the two. Changing "component" to "element" is one of the several differences between the two versions. Here are the docs for the version 5 of React Router.
In your warning it tell you "ReactDOM.render is no longer supported in React 18". You should be using React functional components and not classes with the latest version of react.
In order to use v6, you'll need to convert all your Switch elements to Routes. So if you downgraded from 6 to 5, you can't use Routes.
Here is a v5 example:
<BrowserRouter>
<Switch>
<Route exact path="/">
<Home />
</Route>
<Route path="/users">
<Users />
</Route>
</Switch>
</BrowserRouter>
And here is the same example in v6:
<BrowserRouter>
<Routes>
<Route path="/" element={<Home />} />
<Route path="users/*" element={<Users />} />
</Routes>
</BrowserRouter>
You need to decide what version of everything you are using and stick to that. Several packages have major differences between versions. And you need to make sure that the version you are using of package X is compatible with React version Y.
Related
This question already has answers here:
Error: [PrivateRoute] is not a <Route> component. All component children of <Routes> must be a <Route> or <React.Fragment>
(18 answers)
Closed last year.
How can you render a composed Route component
code example
Bottomline from above example is that in the following code, the Wrapped route will never render it's element
const App = () => (
<Routes>
<Wrapped/>
<Route path="/inline" element={<span >Inline works</span>} />
</Routes>
);
const Wrapped = () => <Route path="/wrapped" element={<span>wrapped</span>} />
Is there a way of doing this kind of composition with the Route component with react-router v6? Or will react-router v6 only support Route directly nested in the Routes component?
Edit, more specifically I'm trying to get a recommendation for using a ProtectedRoute component, something among the lines of:
type Props = {
element: ReactElement;
redirectRoute: string;
} & RouteProps;
const ProtectedRoute = ({element, redirectRoute, ...rest}: Props) => {
const {isAuthenticated} = useAuth();
<Route {...rest} element={isAuthenticated() ? element : <Navigate to={redirectRoute}/>}/>
}
EDIT:
It seems like this used to work in older beta versions, so this might be a bug. At the moment the latest version is 6.0.0-beta.4 &
I've logged an issue:
https://github.com/remix-run/react-router/issues/8066
In your code, you are trying to use Wrapped Component as a Router, but it's not. It's a React element returning a React Router element. Since you only need Router in this simple usecase, you can treat is as a function:
<Suspense fallback={null}>
<Routes>
{/* Use this as a normal function, and also function name etc.,*/}
{Wrapped()}
<Route path="/inline" element={<span>Inline works</span>} />
</Routes>
</Suspense>
However, I would recommend not to complicate the routes by trying to add customizations on route and instead wrap your component that you want to route.
For eg.,
const Wrapped = () => <Route path="/wrapped" element={<span>wrapped</span>} />;
to
<Route path="/wrapped" element={<Wrapped>wrapped component</Wrapped>} />
Or will react-router v6 only support Route directly nested in the Routes component?
Correct, RRv6 does not support route composition. Instead, try using your <Wrapped /> component inside the element prop. E.g.
<Route path="/foo" element={<Wrapped>/* something here */</Wrapped>} />
I've been trying to create a SPA on an electron project using React, React-Router and typescript (boostrapped from here).
I'm currently just trying to make it reroute to a different route via a <Link> but I'm getting an error in the console saying
Uncaught DOMException: Failed to execute 'pushState' on 'History': A history state object with URL 'http://settings/' cannot be created in a document with origin 'http://localhost:2003' and URL 'http://localhost:2003/'.
where localhost:2003 is the port associated with the project. I'm not sure how I can fix this as I believe this is a problem specific with electron? Not sure..
The code I'm running is exactly the same as https://codesandbox.io/s/weathered-river-9yrbv
(please to go /app first in the sandbox)
You mad mistake to load props in App function.
function App(props) {
console.log(props);
return (
<div className="App">
{/* {props} // Remove this line... */}
<h1>Hello CodeSandbox</h1>
<h2>Start editing to see some magic happen!</h2>
<ProtectedRoute path="/app" component={MainComponent} />
</div>
);
}
You have to wrap App component via Route.
import { BrowserRouter as Router, Link, Route } from "react-router-dom";
...
...
render(
<Router>
<Route path="/" component={App}/>
</Router>,
rootElement
);
Now you can get the props in console.log.
Here is the updated working code for you.
https://codesandbox.io/s/elegant-elion-qr1qf
Hope this will work for you!
I'm trying to send the user to a generic error page when the App breaks, thing is I'm trying with the ErrorBoundary method and then rendering out the Redirect;
export default class ErrorBoundary extends Component {
state = { has_error: false }
componentDidCatch(error, info)
this.setState({ has_error: true });
}
render() {
if (this.state.has_error)
return <Redirect to="/somewhere/else" />
}
return this.props.children;
}
};
And then using the ErrorBoundary to wrap all the routes and sub components inside the Router
<Router history={history}>
<ErrorBoundary>
<Header />
<Switch>
<Route exact path="/" component={Home} />
<Route
path="/createManager/:managerId"
component={CreateManager}
/>
<Route path="/login" component={LoginComp} />
<Route path="/test" component={Test} />
<Route path="/register" component={RegisterAccount} />
<Route component={NotFound} />
</Switch>
<Footer />
</ErrorBoundary>
</Router>
The componentDidCatch is never triggered, thus, never leaving the current error page, neither in the dev nor prod version. How can I send the user to a X route when the App breaks or tries to throw an error?
In order to trigger an error, I leave one Component with an empty prop, and later on click trying to use the function that should be passed in the prop.
componentDidCatch only triggers if the error is thrown inside the render method.
The reason why your componentDidCatch is not triggered is because events like onClick is not in the render lifecycle. So componentDidCatch won't be able to catch this kind of error.
One way to test out the your componentDidCatch is throwing an error inside the render method.
For errors outside of the render method you have to be careful and add try/catches in places you think might have errors in your action handlers.
Also a good way to prevent undefined onClick is to add flow or typescript.
https://reactjs.org/blog/2017/07/26/error-handling-in-react-16.html#component-stack-traces
React 16 prints all errors that occurred during rendering to the console in development, even if the application accidentally swallows them. In addition to the error message and the JavaScript stack, it also provides component stack traces. Now you can see where exactly in the component tree the failure has happened:
I have this code:
<BrowserRouter>
<Route path="/(:filter)?" component={App} />
</BrowserRouter>
the filter param or '' on the root is suppose to be on App components' props base on the previous react router versions?
This is my code on my App:
const App = ({params}) => {
return ( // destructure params on props
<div>
<AddTodo />
<VisibleTodoList
filter={params.filter || 'all'} // i want to git filter param or assign 'all' on root
/>
<Footer />
</div>
);
}
I logged this.props.match.params on console but it has none? help?
I assume you are following the Redux tutorial on Egghead.io, as your example code seems to use what is defined in that video series. I also got stuck on this part trying to integrate React Router v4, but eventually found a working solution not far from what you have tried.
⚠️ NOTE: one thing I would check here is that you are using the
current version of react-router-dom (4.1.1 at the time of this
writing). I had some issues with optional filters in the params on
some of the alpha and beta versions of v4.
First, some of the answers here are incorrect, and you indeed can use optional params in React Router v4 without the need for regular expressions, multiple paths, archaic syntax, or using the <Switch> component.
<BrowserRouter>
<Route path="/:filter?" component={App} />
</BrowserRouter>
Second, as others have noted, React Router v4 no longer exposes params on route handler components, but instead gives us a match prop to use.
const App = ({ match }) => {
return (
<div>
<AddTodo />
<VisibleTodoList filter={match.params.filter || 'all'} />
<Footer />
</div>
)
}
From here, there are two ways to keep your content in sync with the current route: using mapStateToProps or using the HoC withRouter, both solutions are already talked about in the Egghead series so I won't recapitulate them here.
If you are still having trouble, I urge you to check out my repository of the completed application from that series.
Here is the commit using the mapStateToProps solution
Here is the commit using the withRouter soluiton
Both of which seem to work fine (I just tested both of them).
React Router v4 does not accept a regex for the path. You won't find regular expressions covered anywhere in the documentation. Instead of a regex you can just create multiple routes inside the <Switch> component and the first one that matches will be rendered:
<BrowserRouter>
<Switch>
<Route path="/" component={App} />
<Route path="/:filter" component={App} />
</Switch>
</BrowserRouter>
You also have a bug in your App component. You get the params via the match property, not the params property (not tested, but this should be closer to what you want):
const App = ({match}) => {
return (
<div>
<AddTodo />
<VisibleTodoList
filter={match.params.filter || 'all'}
/>
<Footer />
</div>
);
}
All of the above is covered in the React Router V4 docs on ambiguous matches
From the react-router documentation, props.match.params is where your parameteres are stored.
So to access the filter name, try this
const App = ({match}) => {
...
<VisibleTodoList
filter={match.params.filter || 'all'}
/>
...
}
I know the question is about v4, but if sm looks for v5, we can use the useParams hook.
// /:filter
const {filter} = useParams();
To get the params in the path URL
//to your route component
<Route path={`${match.url}/upload/:title`} component={FileUpload}/>
=====
//Parent component
<Link to={`${match.url}/upload/css`}>
=====
//Child component
const {params: {title}} = this.props.match;
console.log(title);
result = css
I do not know why react router cannot detect my filter even though it's working properly, I resolved this problem by using location.pathname since it does the work for me. I think react router cannot detect my filter param because of regexp, I expected that so I put || to use all on root, but unfortunately for me I it cannot detect so I used location.pathname . I would appreciate suggestions on this since I am not sure with my path configuration on regexp.
Just upgraded to react-router-dom 4.0.0. All my components are either regular classes or fat arrows. They are all exported using export default ThatComponent. Yet I'm getting this:
Uncaught Error: Element type is invalid: expected a string (for built-in components) or a class/function (for composite components) but got: undefined. You likely forgot to export your component from the file it's defined in. Check the render method of Router.
// minimal showcase
import { BrowserRouter, Match, Miss } from 'react-router';
const Router = () => (
<BrowserRouter>
<div>
{/* both Match and Miss components below cause an error */}
<Match exactly pattern="/login" component={Login} />
<Match exactly pattern="/frontpage" component={Frontpage} />
<Match exactly pattern="/logout" render={() => (<div>logout</div>)} />
<Miss component={NoMatch} />
</div>
</BrowserRouter>
);
Why do the <Match> components think the other components are undefined?
In the final release of react-router there is no Match nor Miss. You just need to use Route. Also, you need to install and import react-router-dom package to use BrowserRouter (see React Router documentation).
To make your example work with minimal changes, you'd need to do the following:
// minimal showcase
import { BrowserRouter, Route, Switch } from 'react-router-dom';
const Router = () => (
<BrowserRouter>
<Switch>
<Route exact path="/login" component={Login} />
<Route exact path="/frontpage" component={Frontpage} />
<Route exact path="/logout" render={() => (<div>logout</div>)} />
<Route component={NoMatch} />
</Switch>
</BrowserRouter>
);
Please have a look at NoMatchExample in React Router docs to see how to and when to use Switch.
Check source of react-router here: https://unpkg.com/react-router#4.0.0/index.js(also https://unpkg.com/react-router-dom#4.0.0/index.js),
There is no Match under it. Match maybe belong to other package.