React router rendering two child components instead of just one - javascript

I'm trying to add routes to my application but for some reason there are two components being rendered to the page instead of just one.
My code looks like this (the relevant part):
import React from "react";
import Board from "./Components/Board";
import Navbar from "./Components/Navbar";
import TaskDetail from "./Components/TaskDetail";
import { LanesProvider } from "./Context/lanes.context";
import { TasksProvider } from "./Context/tasks.context";
import { BrowserRouter, Route, Switch } from "react-router-dom";
function App(props) {
const getTask = props => {
return <TaskDetail />;
};
return (
<>
<LanesProvider>
<TasksProvider>
<Navbar />
<Board />
<BrowserRouter>
<Switch>
<Route exact path="/" render={() => <Board />} />
<Route exact path="/board" render={() => <Board />} />
<Route exact path="/board/:taskName" render={() => getTask()} />
</Switch>
</BrowserRouter>
</TasksProvider>
</LanesProvider>
</>
);
}
Now basically when I'm navigating to "localhost/board/test" I would expect to just see the <TaskDetail/> component but instead I get the <Board /> AND <TaskDetail/>.
I didn't expect this to happen because of the exact boolean.
FYI: getTask() is only returning a component right now because I wanted to get the routes to work first before implementing further logic.
So far I could not find a solution to this.
Thank you in advance.

There is a <Board /> component outside your <BrowserRouter>

import React from "react";
import Board from "./Components/Board";
import Navbar from "./Components/Navbar";
import TaskDetail from "./Components/TaskDetail";
import { LanesProvider } from "./Context/lanes.context";
import { TasksProvider } from "./Context/tasks.context";
import { BrowserRouter, Route, Switch } from "react-router-dom";
function App(props) {
const getTask = props => {
return <TaskDetail />;
};
return (
<>
<LanesProvider>
<TasksProvider>
<Navbar />
<Board /> --> Remove this component from here
<BrowserRouter>
<Switch>
<Route exact path="/" render={() => <Board />} />
<Route exact path="/board" render={() => <Board />} />
<Route exact path="/board/:taskName" render={() => getTask()} />
</Switch>
</BrowserRouter>
</TasksProvider>
</LanesProvider>
</>
);
}

Related

Why React does not render component when I redirect to product page

I have an error and can not find any solution in google. The error appears when I want to go to a poduct page and press a button on home page to go to a product page and there I don't have any element rendered, I used React Route to user be able to go to product page and add it to a cart and suppose I did something wrong with providing path but not sure.
VM867:236 Matched leaf route at location "/2" does not have an element. This means it will render an <Outlet /> with a null value by default resulting in an "empty" page.
Here is a code for Item Element:
import React, { useState, useEffect } from "react";
import { useParams } from "react-router-dom";
function Item() {
const { id } = useParams();
const [item, setItem] = useState([]);
const [loading, setLoading] = useState(false);
console.log("item", item);
// Fetching Data
useEffect(() => {
const fetchedData = async () => {
setLoading(true);
const response = await fetch(`https://fakestoreapi.com/products/${id}`);
const data = response.json();
setItem(data);
};
fetchedData();
}, []);
return (
<div className="container">
{loading ? (
<>
<h3>Loading.....</h3>
</>
) : (
<div className="container">
<p>{item.title}</p>
</div>
)}
</div>
);
}
export default Item;
And for App.js
import { BrowserRouter as Router, Route, Routes } from "react-router-dom";
import ItemsComponent from "./Componets/ItemsComponent";
import Navbar from "./Componets/Navbar";
import Home from "./Componets/Home";
import Item from "./Componets/Item";
import About from "./Componets/About";
import AboutLink from "./Componets/AboutLink";
import Contact from "./Componets/Contact";
import Footer from "./Componets/Footer";
import "./App.css";
function App() {
return (
<>
<Router>
<Navbar />
<Routes>
<Route
exact
path="/"
element={
<>
<Home />
<ItemsComponent />
</>
}
/>
<Route exact path="/:id" component={<Item />} />
<Route exact path="/about" element={<About />} />
</Routes>
<AboutLink />
<Footer />
</Router>
</>
);
}
export default App;
It also affected all styling.
Maybe it's because you have
<Route exact path="/:id" component={<Item />} />
instead of
<Route exact path="/:id" element={<Item />} />
?
I think that instead of this
<Route exact path="/:id" component={<Item />} />
you want either of those
<Route exact path="/:id" component={Item} />
<Route exact path="/:id" element={<Item />} />

Props not pass in the route though pass it in the render

I am trying to pass the props in the route component, I know we cannot directly pass to that, so I used to render, but the props are still undefined in the child component.
import React from 'react';
//components
import Register from '../components/register/register';
import Login from "../components/login/login";
import ForgetPassword from '../components/forget-password/forget-password';
//redux
import {store} from "../redux/store";
import { connect } from 'react-redux';
import actions from "../redux/authentication/actions";
//react-router
import {BrowserRouter,Route,Switch} from "react-router-dom";
//antd
import "antd/dist/antd.css";
//css
import '../global/_global.scss';
function Authentication(props) {
console.log("PROPS", props)
return (
<div className="App">
<BrowserRouter>
{/*switch-component will render first that matches the includes path*/}
<Switch>
<Route path='/login' component={Login} />
<Route exact path='/' component={Login} />
<Route path='/register'
render={(props) => (
<Register {...props} check={props.registerUser} />
)}
/>
<Route path='/forget-password' component={ForgetPassword} />
</Switch>
</BrowserRouter>
</div>
);
}
function mapStateToProps(state){
return state.reducers;
}
export default connect(mapStateToProps, actions)(Authentication);
try using like this
return (
<Route
render={routeProps => (
<Component {...routeProps} />
)}
/>
);
instead of
return (
<Route
render={(routeProps) => (
<Component {...routeProps} />
)}
/>
);

Why you can't render from 2 JSX element?

My goal is to render <Route /> with array.map method. Right now, I am trying to pass the return value using react hook, but the problem is localhost:3000/login return <div>Login</div> while localhost:3000/dashboard return nothing.
My expected result is when I visit localhost:3000/dashboard the JSX.element return <div>Dashboard</div>
App.js
import React from "react";
import { BrowserRouter as Router, Switch, Route } from "react-router-dom";
const PublicRouteComponents = () => {
return <Route exact path="/login" render={() => <div>Login</div>} />
};
const PrivateRouteComponents = () => {
return <Route exact path="/dashboard" render={() => <div>Dashboard</div>} />
};
function App() {
return (
<Router>
<Switch>
{/* <Route exact path="/login" render={() => <div>Login</div>} />
<Route exact path="/dashboard" render={() => <div>Dashboard</div>} /> */}
<PublicRouteComponents />
<PrivateRouteComponents />
</Switch>
</Router>
);
}
export default App;
edit:
the current solution is to give up the react hook and went straight to JSX.element
function App() {
const PublicRouteComponents = PublicRoutes.map(
({restricted, component, path}, key) => <PublicRoute restricted={restricted} component={component} exact path={path} key={key} />
)
const PrivateRouteComponents = PrivateRoutes.map(
({component, path}, key) => <PrivateRoute component={component} exact path={path} key={key} />
)
return (
<Router>
<Switch>
{PublicRouteComponents}
{PrivateRouteComponents}
</Switch>
</Router>
);
}
But, my instructor told me, if you gave an expression instead of class, when the component inside {PublicRouteComponents} changes, the app will got re-rendered. Instead if you use <PublicRouteComponents />, when the class change, only <PublicRouteComponents /> will be re-rendered.
I wish to achieve that.
After playing around a bit, I found the underlying issue in your first code sample.
Basically, React router demands that the route path and the exact props to be set in the children of the Router component.
import React from "react";
import { BrowserRouter as Router, Switch, Route } from "react-router-dom";
const LoginRoute = () => {
return <Route render={() => <div>Login</div>} />;
};
const DashboardRoute = () => {
return <Route render={() => <div>Dashboard</div>} />;
};
export default function App() {
return (
<Router>
<Switch>
<DashboardRoute exact path="/dashboard" />
<LoginRoute exact path="/login" />
</Switch>
</Router>
);
}
If you move the path and exact props to the DashboardRoute and LoginRoute components, then react router is unable to match those routes.
See codesandbox

Redirect all url with # from HashRouter to BrowserRouter

I changed HashRouter to BrowserRouter and now I would like to redirect all urls to remove # from them. (Url are in e.g. mailing - so I have to do it).
I found a similar topic to this, but none of the solutions there works for me.
import { BrowserRouter } from 'react-router-dom'
class Index extends Component {
render() {
return (
<BrowserRouter>
<Switch>
<Route exact path={"/"} component={() => <HomePage />}/>
<Redirect from='/#/bus/:category' to '/bus/:category' />
<Route exact path='/bus/:category' component={BusCategory} />
</Switch>
</BrowserRouter>
)
}
}
ReactDOM.render(<Index />, document.getElementById("index"));
It's redirecting only to HomePage.
Next solution also not working:
import { BrowserRouter } from 'react-router-dom'
class Index extends Component {
render() {
const history = useHistory()
if (location.hash.startsWith('#/')) {
history.push(location.hash.replace('#', '')) // or history.replace
}
return (
<BrowserRouter>
<Switch>
<Route exact path={"/"} component={() => <HomePage />}/>
<Route exact path='/bus/:category' component={BusCategory} />
</Switch>
</BrowserRouter>
)
}
}
ReactDOM.render(<Index />, document.getElementById("index"));
and the last one also nothning :(
import { BrowserRouter } from 'react-router-dom'
class Index extends Component {
render() {
return (
<BrowserRouter>
<Switch>
<Route exact path={"/"} component={() => <HomePage />}/>
<Route exact path='/bus/:category' component={BusCategory} />
<Route path={"/bus/:category"} render={({ location }) => <Redirect strict to={location.hash.replace('#', '')} />} />
</Switch>
</BrowserRouter>
)
}
}
ReactDOM.render(<Index />, document.getElementById("index"));
I've probably already tried all the options, if anyone can help me I'll be extremely grateful.
// EDIT
now everything works, except the HomePage...
import { BrowserRouter } from 'react-router-dom'
class Index extends Component {
render() {
return (
<BrowserRouter>
<Switch>
<Route path={"/bus/:category"} render={({ location }) => <Redirect strict to={location.hash.replace('#', '')} />} />
<Route exact path={"/"} component={() => <HomePage />}/>
<Route exact path='/bus/:category' component={BusCategory} />
</Switch>
</BrowserRouter>
)
}
}
ReactDOM.render(<Index />, document.getElementById("index"));
You can use something like
import React, { useEffect } from 'react'
import { BrowserRouter, Route, useHistory, useLocation } from 'react-router-dom'
const Index = () => {
const history = useHistory()
const location = useLocation()
useEffect(() => {
if (location.hash) {
history.replace(location.hash.replace('#', ''))
}
}, [history, location.hash, location.pathname])
return (
<>
<Route exact path={'/'} render={() => <h1>Home</h1>} />
<Route
exact
path="/bus/:category"
render={({ match: { params } }) => <div>{params.category}</div>}
/>
</>
)
}
export default () => (
<BrowserRouter>
<Index />
</BrowserRouter>
)
Your previous code didn't work because <Index /> component was not the child of BrowserRouter component, because of which the location, as well as history, would be undefined.
Now if you try to visit localhost:3000/#/bus/car it would redirect to localhost:3000/bus/car.
Two points:
Why do you have to do a redirect at all? If I understand you correctly, you only used the BrowserRouter instead of the HashRouter. Exchanged, so to speak. This automatically eliminates the # and the pages "/#/bus/:category" simply no longer exist.
Is the line <Redirect from='/#/bus/:category' to '/bus/category' /> really correct? When I read this, it looks to me like you are referring to the URL "/bus/category". But actually you want something like "/bus/cars".

Programmatically navigate while using HashRouter

I'm using HashRouter for my routes in a react.js app. I then have a function which does the following:
this.props.history.push('/somePath');
The problem is, ever since I started using HashRouter, the page doesn't push to that path when that function gets called.
I logged this.props.history, and push was there.
How can I programmatically navigate while using HashRouter?
Note: I used withRouter
Here's the code:
import React, { Component } from 'react';
import { HashRouter, Route, Switch, Redirect, withRouter } from 'react-router-dom';
// Other imports
class App extends Component {
navigate = () => {
this.props.history.push('/route1');
console.log(this.props.history);
};
render() {
return (
<div className="App">
<Header />
<HashRouter>
<Switch>
<Route
path="/route1"
exact
render={() => (
<FirstRoute someSetting='setting1'/>
)}
/>
<Route
path="/route2"
exact
render={() => (
<SecondRoute anotherSetting='Really Cool'/>
)}
/>
<Route path="/404" component={NotFound} />
<Route exact path="/" render={() => <HomePage someSettings='Home Page' />} />
<Redirect to="/404" />
</Switch>
</HashRouter>
<BottomBar />
</div>
);
}
}
export default withRouter(App);

Categories

Resources