Error when using Render method but not component in Route [closed] - reactjs

Closed. This question is not reproducible or was caused by typos. It is not currently accepting answers.
This question was caused by a typo or a problem that can no longer be reproduced. While similar questions may be on-topic here, this one was resolved in a way less likely to help future readers.
Closed 3 years ago.
Improve this question
I am currently running into the 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."
I have searched the error up online and see that most of the time it is caused by someone incorrectly exporting or importing a component but I cannot seem to find where I have done that wrong.
Still, I have managed to find a way to render my component. When I use
PrivateRoute path="/protected" component={Protected} it works, but if I use PrivateRoute with render (render={() => < Protected/>}) it seems to give me the error. I am assuming it has to do with either the PrivateRoute or how im calling the component. Im going to include my dependencies to make sure that I am not calling it wrong. Also here is a link to my codesandbox: https://codesandbox.io/s/dawn-cherry-3yv8e?fontsize=14.
import React from "react";
import ReactDOM from "react-dom";
import {
BrowserRouter as Router,
Route,
Link,
Switch,
Redirect,
withRouter
} from "react-router-dom";
import "./styles.css";
import PrivateRoute from "./PrivateRoute";
import Login from "./Login";
function Protected() {
return <h3>Protected</h3>;
}
function Public() {
return <h3>public</h3>;
}
class App extends React.Component {
render() {
return (
<React.Fragment>
<Router>
<div>
<ul>
<li>
<Link to="/public">Public Page</Link>
</li>
<li>
<Link to="/protected">Protected Page</Link>
</li>
</ul>
<Switch>
<Route path="/public" component={Public} />
<Route path="/login" render={() => <Login />} exact />
<PrivateRoute
path="/protected"
// component={Protected}
render={() => <Protected />}
exact
/>
</Switch>
</div>
</Router>
</React.Fragment>
);
}
}
import React from "react";
import { Route, Redirect } from "react-router-dom";
import fakeAuth from "./fakeAuth";
function PrivateRoute({ component: Component, ...rest }) {
return (
<Route
{...rest}
render={props =>
fakeAuth.isAuthenticated ? (
<Component {...props} />
) : (
<Redirect
to={{
pathname: "/login",
state: { from: props.location }
}}
/>
)
}
/>
);
}
export default PrivateRoute;
If you would like to see the rest of the code check the codesandbox that is linked above. Thanks for any help

You should name your PrivateRoute prop accordingly, quick fix:
<PrivateRoute
path="/protected"
toRender={Protected}
// toRender={() => <Protected />} // This should also work
exact
/>
// Here we use render prop as Component
function PrivateRoute({ toRender: Component, ...rest }) {
return (
<Route
{...rest}
render={props => (
fakeAuth.isAuthenticated
? (
<Component {...props} />
)
: (
<Redirect
to={{
pathname: "/login",
state: { from: props.location }
}}
/>
)
)}
/>
);
}
Working example

Related

How do I add pages in react [closed]

Closed. This question needs to be more focused. It is not currently accepting answers.
Want to improve this question? Update the question so it focuses on one problem only by editing this post.
Closed 7 months ago.
Improve this question
I'm new to web development but I've built a website using react and I can't figure out how to add a page to it. Do I add it in app.js or do I need another file? Are there any examples that can explain this?
For adding a page, You need to install and use the react-router-dom in your react application.
Initially, You need to install react-router-dom. Run a below command to install the same,
npm install react-router-dom
Then configure the routing in App.js. Each route defines a seperate page. The Layout route alone will be in the top layer. That is for Layout setup. We will wrapping all the routes inside the Layout route and in the Layout component we will be providing where the contents of Home and About will be displayed.
App.js
import './App.css';
import { BrowserRouter, Routes, Route } from 'react-router-dom'
import Home from './Components/Home'
import About from './Components/About'
import Layout from './Components/Layout'
function App() {
return (
<div className="App">
<BrowserRouter>
<Routes>
<Route path="/" element={<Layout />}>
<Route path="/" element={<Home />} />
<Route path="about" element={<About />} />
</Route>
</Routes>
</BrowserRouter>
</div>
);
}
export default App;
Layout.jsx
import { Outlet, Link } from 'react-router-dom'
export default function Layout() {
return (
<>
<Link to="/">Home</Link>
<Link to="/about">About</Link>
<Outlet />
</>
)
}
Home.jsx
export default function Home() {
return (
<>
<p>This is Home</p>
</>
)
}
About.jsx
export default function About() {
return (
<>
<p>This is About Us</p>
</>
)
}
You can create components or separate pages based on your requirement. For routing-related stuff, you can use react-router-dom https://v5.reactrouter.com/web/guides/quick-start.
Example :
App.js
import React from "react";
import {
BrowserRouter as Router,
Switch,
Route,
Link
} from "react-router-dom";
export default function App() {
return (
<Router>
<div>
<nav>
<ul>
<li>
<Link to="/">Home</Link>
</li>
<li>
<Link to="/about">About</Link>
</li>
<li>
<Link to="/users">Users</Link>
</li>
</ul>
</nav>
{/* A <Switch> looks through its children <Route>s and
renders the first one that matches the current URL. */}
<Switch>
<Route path="/about">
<About />
</Route>
<Route path="/users">
<Users />
</Route>
<Route path="/">
<Home />
</Route>
</Switch>
</div>
</Router>
);
}
Home.js
function Home() {
return <h2>Home</h2>;
}
About.js
function About() {
return <h2>About</h2>;
}
Users.js
function Users() {
return <h2>Users</h2>;
}

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

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)
How to create a protected route with react-router-dom?
(5 answers)
Closed 8 months ago.
I know there are questions on stackoverflow that solve similar problems. I tried the answers to this question but without success. Here is the content of my base ProtectedRoute.js file:
import React from 'react'
import { Navigate, Route } from 'react-router-dom'
function ProtectedRoute({ component: Component, isLoggedIn, ...rest }) {
return (
<Route
{...rest}
render={(props) => {
if (isLoggedIn) {
return <Component {...rest} {...props} />
} else {
return (
<Navigate to={{ pathname: '/', state: { from: props.location } }} />
)
}
}}
/>
)
}
export default ProtectedRoute
and the same file after testing the answers from the question linked above:
import React from 'react'
import { Navigate } from 'react-router-dom'
const ProtectedRoute = ( Component, isLoggedIn, props, ...rest ) => {
// determine if authorized, from context or however you're doing it
// If authorized, return an outlet that will render child elements
// If not, return element that will navigate to login page
return isLoggedIn ? <Component {...rest} {...props} /> : <Navigate to={{ pathname: '/', state: { from: props.location } }} />;
}
export default ProtectedRoute
The content of my App.js file is:
...
<Routes>
<Route
exact
path="/"
render={(props) =>
!userState.isLoggedIn ? (
<Auth />
) : (
< Navigate to="/home" />
)
}
/>
<ProtectedRoute
exact
path="/friends"
component={Friends}
isLoggedIn={userState.isLoggedIn}
/>
...
Despite all my changes to the ProtectedRoute.js file I get the same error.

React Router Render props is not rendering component when using params

this is my first post in Stackoverflow and I hope you guys can give me some guidance.
I'm currently learning React Router and it seems that I got stucked on an error or bug that I can't find to solve. To give you a little overview about the setup, here it is:
I'm using CRA with React-Router-DOM 5.0.1.
I'm using React Hooks to practice and using latest version of React 16.8.6
This is an app that reads a local API and it renders its data on different components. Here is the code:
App.js
import React, { Fragment, useState, useEffect } from 'react';
import { BrowserRouter, Link, Route, Switch } from 'react-router-dom';
import axios from 'axios';
import Writers from './Writers';
function App() {
const [writers, setWriters] = useState([]);
useEffect(() => {
const fetchData = async () => {
const result = await axios('http://localhost:3004/writers');
setWriters(result.data);
};
fetchData();
}, []);
return (
<BrowserRouter>
<Fragment>
<ul>
<li>
<Link to='/'>Home</Link>
</li>
<li>
<Link to='/writers'>Writers</Link>
</li>
</ul>
<Switch>
<Route exact path='/' render={() => <div>Home</div>} />
<Route
exact
path='/writers'
render={props => <Writers {...props} writers={writers} />}
/>
</Switch>
</Fragment>
</BrowserRouter>
);
}
export default App;
Writers.js
import React, { Fragment } from 'react';
import { Route, Link } from 'react-router-dom';
import Writer from './Writer/index';
const Writers = props => {
console.log(props);
return (
<Fragment>
<ul>
{props.writers.map(({ id, name }) => (
<li key={id}>
<Link to={`${props.match.url}/${id}`}>{name}</Link>
</li>
))}
</ul>
<Route
path={`${props.match.url}/:writerID`}
render={props => <Writer {...props} />}
/>
</Fragment>
);
};
export default Writers;
Writer.js
import React, { Fragment } from 'react';
const Writer = ({ match, id, name, description }) => {
console.log(match);
return <Fragment>id name description</Fragment>;
};
export default Writer;
So I'm having an issue in Writers.js where I'm passing the params "/:writerID", this params doesn't get to Writer component and actually Writer component never gets rendered at all
<Route
path={`${props.match.url}/:writerID`}
render={props => <Writer {...props} />}
/>
If I remove :writerID from the path prop, the component Writer gets rendered BUT it doesn't allow me route it the unique path that I'm looking for.
Any idea why this might be happening? Any help will be greatly appreciated.
I have tried making sure I have the correct exports on my files and they are, in fact, I don't see any errors from the CRA logs.
Also, I remove the params from the path props and it seems that the Writer components renders but if I put it again, it doesn't work.
App.js passing Writers with props and writers data
<Route
exact
path='/writers'
render={props => <Writers {...props} writers={writers} />}
/>
Writers.js receives the data and Route props so I can access Match props but this component does NOT render Writer component neither get the match.params prop.
<Route
path={`${props.match.url}/:writerID`}
render={props => <Writer {...props} />}
/>
Expected behavior: Writer.js should be render when trying to click on any individual writer and it should allow me to get the match.params prop.
Actual Behavior: Writer.js does not gets rendered and if I remove the path prop from Route, it gets render but I can't access the individual id.
In your Route path for /writers you are using the exact prop, which will lead to any nested Route not being rendered. Remove the exact prop and your Routes will work
<Route
exact
path='/writers'
render={props => <Writers {...props} writers={writers} />}
/>
Also as a thumb rule you should use match.url for Link path and match.path for a Route path

Problem using Render instead of Component in React

I get this 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
When clicking this link:
<li><NavLink activeClassName="active" to={{pathname: '/add-new'}}>Add new Recipe</NavLink>{' '} </li>
My route looks like this:
<PrivateRoute
exact
path='/add-new'
render={(props) =>
<NewRecipe
{...props}
addRecipe={this.addRecipe}/>
}
authenticated={authenticated}
/>
PrivateRoute.js looks like this:
import React from "react";
import { Route, Redirect } from "react-router-dom";
export default function PrivateRoute({
component: Component,
authenticated,
...rest
}) {
console.log(...rest);
return (
<Route
{...rest}
render={props =>
authenticated === true ? (
<Component {...props} {...rest} />
) : (
<Redirect to="/login" />
)
}
/>
);
}
Error occurs since I switched from Component={NewRecipe} to Render={...} since I need to pass a function.
PrivateRoute skips render props (instead of call it), fix might be something like this (notice render() logic):
export default function PrivateRoute({
component: Component,
authenticated,
render,
...rest
}) {
return (
<Route
{...rest}
render={props =>
authenticated === true ? (
render ? render(props) : <Component {...props} {...rest} />
) : (
<Redirect to="/login" />
)
}
/>
);
It's overcomplex a bit, but I hope can help you understand the idea on how render prop might be caught.
Other solution is to change /add-new to be passed as a component:
<PrivateRoute
exact
path='/add-new'
addRecipe={this.addRecipe}
component={NewRecipe}
authenticated={authenticated}
/>

Simple Conditional Routing in Reactjs

How to implement conditional routing i.e. if and only if some conditions satisfies, then routing should occur.
For example, if and only if the user enters the correct credentials, login should be successful and the user should be able to see the welcome page.
If we directly hit some URL like localhost:8080/welcome, that should not be navigated to welcome page. The welcome page should only be displayed after login.
How to achieve this, can anyone help me please?
App.js
import React, { Component } from 'react';
import Header from './Header';
class App extends Component{
render(){
return(
<div>
<Header />
</div>
);
}
}
export default App;
Header.js
import React, { Component } from 'react';
import {Link} from 'react-router-dom';
import Login from './Login';
import SignUp from './SignUp';
class Header extends Component{
render(){
return(
<div>
<nav class="navbar navbar-default">
<div class="container-fluid">
<ul class="nav navbar-nav">
<li><Link to={Login}>Login</Link></li>
<li><Link to={Login}>SignUp</Link></li>
</ul>
</div>
</nav>
</div>
);
}
}
export default Header;
AllRoutes.js
import React, { Component } from 'react';
import { Switch, Route } from 'react-router-dom';
import Login from './Login';
import SignUp from './SignUp';
import Welcome from './Welcome';
class AllRoutes extends Component{
render(){
return(
<Switch>
<Route exact path="/login" component={Login} />
<Route exact path="/signup" component={SignUp} />
<Route exact path="/Welcome" component={Welcome} />
</Switch>
);
}
}
export default AllRoutes;
Welcome.js
import React, { Component } from 'react';
class Welcome extends Component{
render(){
return(
<div>
<h2>Welcome to MainPage..</h2>
</div>
);
}
}
export default Welcome;
To help answer your question, I think you may need to also ask how that route should get blocked. Looking through the example above, you don't yet have a mechanism that helps answer the question of "should I be able to visit this page". That might come from state, redux, or some other means of determining if the user is logged in.
Since react-router is just plain React (one of my favorite parts!!) you have all the tools available to you that you would to conditionally show any part of your React app.
Here are a couple examples of how you might achieve this (by no means is this exhaustive. Be creative! It all depends on your requirements and the tools you are using)
class AllRoutes extends Component{
render(){
return(
<Switch>
<Route exact path="/login" component={Login} />
<Route exact path="/signup" component={SignUp} />
{ this.state.authenticated &&
<Route exact path="/Welcome" component={Welcome} />
}
</Switch>
);
}
}
One of my favorite ways to accomplish this is creating a ProtectedRoute component
class ProtectedRoute extends Component {
render() {
const { component: Component, ...props } = this.props
return (
<Route
{...props}
render={props => (
this.state.authenticated ?
<Component {...props} /> :
<Redirect to='/login' />
)}
/>
)
}
}
class AllRoutes extends Component {
render() {
return (
<Switch>
<Route path='/login' component={Login} />
<ProtectedRoute path='/welcome' component={Welcome} />
</Switch>
)
}
}
While I didn't include any specific logic to how state.authenticated was set, this may come from anywhere (by no means does it needs to come from state). Do your best to answer the question of "how do I determine whether a user is authenticated" and use that mechanism as the means to handle route authentication.
For that you need to break the entire app into two parts, normally accessible and protected part. Protected part will be accessible only after successful login.
To achieve that functionality, create a wrapper of protected part, and define its routes with path='/', and put the condition inside that. All the protected routes should be defined inside that wrapper component. If anyone try to access those routes without login, wrapper will redirect them to login page.
Like this:
class AllRoutes extends Component{
render(){
return(
<Switch>
<Route exact path="/login" component={Login} />
<Route exact path="/signup" component={SignUp} />
<Route path="/" component={AppWrapper} />
</Switch>
);
}
}
AppWrapper Component (assuming you are using some way to maintain whether user is logged-in or not, so put the proper check in if condition):
import { Redirect } from 'react-router-dom'
class AppWrapper extends Component{
render(){
if(/*not login*/)
return <Redirect to="/login" />
return(
<div>
App wrapper
<Route path='/Welcome' component={Welcome} />
</div>
);
}
}
I would like to join the party with simple solution.
Just conditional render in the component prop in as follows:
<Router>
<Navigation />
<Switch>
<Route
exact
path="/"
component={
loading
? () => <div>Loading posts...</div>
: () => <Home posts={posts} />
}
/>
<Route path="/login" component={Login} />
</Switch>
</Router>
Here i am trying to fetch some data from an api when it fetched (loading) should be false and renders Home component.
You can do something like:
let redirectToUrl;
if ( not logged in ) //check condition
{
redirectToUrl = <Redirect to={loginPage}/>;
}
and use the same:
<Router>
<div>
{redirectToUrl}
<Switch>
<Route />
</switch>
</div>
</Router>
For the same you need to import from react-router-dom:
import {
BrowserRouter as Router,
Route,
browserHistory,
Redirect,
Link,
Switch
} from "react-router-dom";
Best way is to create a HOC.
Considering you are maintaining auth state in your redux store. Or else you can check with your own variable.
Create requireAuth.js
import React, { Component } from 'react';
import { connect } from 'react-redux';
export default function(ComposedComponent) {
class Authentication extends Component {
static contextTypes = {
router: React.PropTypes.object
}
componentWillMount() {
if (!this.props.authenticated) {
this.context.router.push('/');
}
}
componentWillUpdate(nextProps) {
if (!nextProps.authenticated) {
this.context.router.push('/');
}
}
render() {
return <ComposedComponent {...this.props} />
}
}
function mapStateToProps(state) {
return { authenticated: state.auth.authenticated };
}
return connect(mapStateToProps)(Authentication);
}
Now in the routes you can use this hoc and pass the component.
import RequireAuth from './requireAuth';
...
<Route exact path="/Welcome" component={RequireAuth(Welcome)} />
The best and simple thing you can do is to create a state variable login and route based on the boolean values. the logic to set is up to you. i can show an example of simple routing based on condition. I store my pages in a array and use the map function to switch to different routes. For an example I have inserted my DesignerHome.js for your reference
This is my App.js
import React,{Component} from 'react';
import{BrowserRouter as Router,Switch,Route,Redirect,} from 'react-router-dom'
import MainHome from './MainHome'
import DesignerHome from './designer/DesignerHome'
export default class App extends Component{
constructor(){
super()
this.state={
login : true,
r_page :[
{
path :'/designerhome',
component : DesignerHome,
},]
}
}
render(){
return(
<Router>
<Switch >
<Route path='/' exact component={MainHome}/>
{this.state.r_page.map((item , i)=>(this.state.login?
<Route exact {...item}/> : <Redirect to="/" /> ))}
</Switch>
</Router>
)
}
}
This is my DesignerHome.js
import React,{Component} from 'react';
export default class DesignerHome extends Component{
render(){
return(
<div>
designer home
</div>
)
}
}
create a state for authentication. based on that navigate to the page.
Also I used render instead of component in Route.
import React, { Fragment, useState, useEffect } from "react";
import Dashboard from "./components/Dashboard";
import Login from "./components/Login";
import Register from "./components/Register";
import {
BrowserRouter as Router,
Switch,
Route,
Redirect,
} from "react-router-dom";
function App() {
const [isAuthenticated, setIsAuthenticated] = useState(false);
const setAuth = (boolean) => {
setIsAuthenticated(boolean);
};
useEffect(() => {
isAuth(); // to be implemented
}, []);
return (
<Fragment>
<Router>
<div className="container">
<NavigationCard />
<Switch>
<Route
exact
path="/login"
render={(props) =>
!isAuthenticated ? (
<Login {...props} setAuth={setAuth} />
) : (
<Redirect to="/dashboard" />
)
}
/>
<Route
exact
path="/register"
render={(props) =>
!isAuthenticated ? (
<Register {...props} setAuth={setAuth} />
) : (
<Redirect to="/login" />
)
}
/>
<Route
exact
path="/dashboard"
render={(props) =>
isAuthenticated ? (
<Dashboard {...props} setAuth={setAuth} />
) : (
<Redirect to="/login" />
)
}
/>
</Switch>
</div>
</Router>
</Fragment>
);
}
export default App;

Resources