I am trying to do a basic router with history, I have:
import {BrowserRouter, Route} from 'react-router-dom';
import createBrowserHistory from 'history/createBrowserHistory'
...
render() {
return (
<BrowserRouter>
<Switch>
<Route path="/login" component={PageA}/>
<Route path="/dashboard" component={PageB}/>
<Route path="/companies" component={PageC}/>
</Switch>
</BrowserRouter>
)
}
}
ReactDOM.render(<App />, document.getElementById('app'))
But I keep getting:
Uncaught Error: React.Children.only expected to receive a single React element child
The error is repeated four times in console.
I have followed a number of other answers on forums but it still doesn't work.
What is wrong with this?
To keep things clean:
You should create a history.js in the src like so:
import createHistory from 'history/createBrowserHistory';
export default createHistory();
In your index.js import Router not BrowserRouter and pass the history as a prop like so:
import {Router, Route, Switch} from 'react-router-dom';
import history from '../history' // relative path to the file you created above...
...
render() {
return (
<Router history={history}> // pass it as a prop here...
<Switch>
<Route path="/login" component={PageA}/>
<Route path="/dashboard" component={PageB}/>
<Route path="/companies" component={PageC}/>
</Switch>
</Router>
)
}
}
Now you can use history.push('/path_you_want') to redirect after an action etc...
Side-Note: This is probably overkill if you're not using redux... For simpler redirects look into the <Redirect /> component offered by the library... more info with examples: https://reacttraining.com/react-router/web/api/Redirect
Related
I'm currently working on a personal project just to get a bit better using react.
I use react-router for navigation. My index.js file has the following
import { render } from "react-dom";
import App from "./App";
import { BrowserRouter, Route, Routes } from "react-router-dom";
import TukTukTours from "./components/TukTukTours";
import BycicleTours from "./components/BicycleTours";
import WalkingTours from "./components/WalkingTours";
const rootElement = document.getElementById("root");
render(
<BrowserRouter>
<Routes>
<Route path="/" element={<App />} />
<Route path='tuktuktours' element={<TukTukTours/>}/>
<Route path='bycicletours' element={<BycicleTours/>}/>
<Route path='walkingtours' element={<WalkingTours/>}/>
</Routes>
</BrowserRouter>,
rootElement
);
The App component is what loads the main page basically, I have a nav bar there which has a select box that changes between two languages. Once the user changes all the text in the page changes from one language to another. I want the same to happen on my three other pages (Routes above).
For that I want to pass down a state variable to App, WalkingTours, BycicleTours and TukTukTours to dynamically change the content of the pages.
The way I have index.js structured it doesn't look like I can use useState hook to achieve what I want. Something similar to the below;
import { render } from "react-dom";
import App from "./App";
import { BrowserRouter, Route, Routes } from "react-router-dom";
import TukTukTours from "./components/TukTukTours";
import BycicleTours from "./components/BicycleTours";
import WalkingTours from "./components/WalkingTours";
import { useState } from "react";
const [language, setLanguage] = useState("GB");
const changeLanguage = (language) => setLang(language);
const rootElement = document.getElementById("root");
render(
<BrowserRouter>
<Routes>
<Route path="/" element={<App language={language} setLang={changeLanguage} />} />
<Route path='tuktuktours' element={<TukTukTours language={language}/>}/>
<Route path='bycicletours' element={<BycicleTours language={language}/>}/>
<Route path='walkingtours' element={<WalkingTours language={language}/>}/>
</Routes>
</BrowserRouter>,
rootElement
);
I know the above is not valid because I can't use hooks outside react function components or custom hook functions.
What would be the best way to achieve what I have above?
Many thanks
I have made use of react js memory router as below in my App.js -
import logo from './logo.svg';
import './App.css';
import './Components/Login'
import Login from './Components/Login';
import {useSelector} from 'react-redux';
import { useEffect } from 'react';
import Welcome from './Components/Welcome';
import{MemoryRouter as Router, Route, Switch} from 'react-router-dom'
function App() {
const state = useSelector(state => state.allReducers)
console.log(state.user.isValid);
return (
<Router>
<Switch>
<Route exact="/" component={Login}></Route>
<Route exact="/" component={Welcome}></Route>
</Switch>
<div className="App">
{state.user.isValid==false ||state.user.isValid== undefined ? <Login></Login> : <Welcome name={state.user.userName}></Welcome>}
</div>
</Router>
);
}
export default App;
But this is displaying my Login component twice on the screen.
How can I avoid this?
Your route configs should be:
<Switch>
<Route path="/login" component={Login} />
<Route path="/" component={Welcome} exact={true} />
</Switch>
exact should be a boolean value. And it'll tell the router only render the route match exactly with the URL. It means, the router only renders the Welcome component when the user stays at /.
But after you changed to my suggestion, you still see 2 login forms if you navigate to /login. Because the div.app will be rendered for every route :D
You're using the same route for both components: <Route exact="/"... so React will show you both. Use different routes for each component. And your syntax is a bit wrong. Like this:
<Switch>
<Route exact path="/"><Welcome /></Route>
<Route exact path="/login"><Login /></Route>
</Switch>
I have little issue I face the first time. I'm trying to use a simple useState but for some reason I can't understand why React throws me back an error and whatever I'm trying to do-nothing fix it.
that's the image of the error:
error description:
Error: Invalid hook call. Hooks can only be called inside of the body of a function component. This could happen for one of the following reasons:
1. You might have mismatching versions of React and the renderer (such as React DOM)
2. You might be breaking the Rules of Hooks
3. You might have more than one copy of React in the same app
my code:(very simple)
import React, {useState} from "react";
function Login() {
const [user, setUser] = useState({});
return <div> why error? </div>;
}
export default Login;
Tried following their solution with no luck of succeeding... thanks
EDIT: this is where I render the component -
import React from "react";
import { BrowserRouter as Router, Switch, Route } from "react-router-dom";
import Home from "./components/Home";
import Contact from "./components/Contact";
import Login from "./components/Login";
import Register from "./components/Register";
import NotFound from "./components/NotFound";
import Navbar from "./components/Navbar";
function App() {
return (
<Router>
<div className="App">
<Navbar />
<Switch>
<Route path="/" exact render={Home} />
<Route path="/contact" exact component={Contact} />
<Route path="/login" exact render={Login} />
<Route path="/register" exact render={Register} />
<Route render={NotFound} />
</Switch>
</div>
</Router>
);
}
...
The issue here is how you're rendering your component. You can't do:
Login()
but you can do:
<Login />
Update: similarly, you can't do:
<Route path="/login" exact render={Login} />
you need:
<Route path="/login" exact render={() => <Login />} />
The function itself has no errors. Tried this in another component in a different file. Check for errors elsewhere. What were the other 40 lines in the code?
And what's the version of React?
I was using the following code from an online course for React routing:
import { Router, Route, browserHistory } from 'react-router';
ReactDOM.render(
<Router history={browserHistory}>
<Route path="/" component={App></Route>
<Route path="/One" component={One}></Route>
<Route path="/Two" component={Two}></Route>
</Router>, document.getElementById('root'));
It gave me a following error 'react-router' does not contain an export named 'browserHistory'.
I did some research and found that I was using React Router v4, and the above code was for v3, so i found that I should be using <BrowserRouter> instead of <Router> so I changed my code to:
import { BrowserRouter, Route, Switch } from 'react-router-dom';
ReactDOM.render(
<BrowserRouter history={History}>
<div>
<Route path="/" component={App}></Route>
<Route path="/One" component={One}></Route>
<Route path="/Two" component={Two}></Route>
<Route path="*" component={NoMatch}></Route>
</div></BrowserRouter>, document.getElementById('root'));
History I have in a separate file:
import { createBrowserHistory } from 'history'
export default createBrowserHistory();
Now the page loads without an error but if I use Developer tools, I can see a warning:
Warning: <BrowserRouter> ignores the history prop. To use a custom history, use `import { Router }` instead of `import { BrowserRouter as Router }`."
Another issue is that <Route path="*" component="NoMatch}></Route> only supposed to load NoMatch component when no path specified in the router but it loads on every page, regardless.
Can anyone help figure out how can I fix the issues?
I am assuming you are using react-router v4
Firstly, Instead of <BrowserRouter> make use of <Router>
import { Router } from 'react-router-dom';
You can get access to the history object's properties by using <withRouter> as mentioned Here
Export the component using <withRouter> , something like :
import React, { Component } from 'react';
import { withRouter } from 'react-router-dom';
class SomeComponent extends Component {
render() {}
}
export default withRouter(SomeComponent);
Secondly , you can make use of <Switch> as it renders the first child <Route> or <Redirect> that matches the location
And you can wrap a <Switch> in a <div> like this:
<Router>
<div>
<Switch>
<Route path="/" component={App}></Route>
<Route path="/One" component={One}></Route>
<Route path="/Two" component={Two}></Route>
<Route component={NoMatch}></Route>
</Switch>
</div>
</Router>
NOTE : Last Route doesn't have path="*"
You can read more about <Switch> on a ReactTraining Github
If you want to read more about React Router V4 or <withRouter> , You can read on this Medium Article
You can only use history with <Router> hence the error message.
See the API on the sidebar in react-router docs.
Browser Router
<BrowserRouter>
basename: string
getUserConfirmation: func
forceRefresh: bool
keyLength: number
children: node
Router
<Router>
history: object
children: node
https://reacttraining.com/react-router/web/api/Router/history-object
As for the no match, you need a switch and to put the last component without a path. Right now you are grabbing every route with path="*"
Again, see docs https://reacttraining.com/react-router/web/example/no-match
<Switch>
<Route path="/" exact component={Home}/>
<Redirect from="/old-match" to="/will-match"/>
<Route path="/will-match" component={WillMatch}/>
<Route component={NoMatch}/>
</Switch>
I have server side rendering app and using react-router for routing. I was using Router.Run Before as the method is no more I am using Router.Match.Previously when there was route change router.run used to be called but same behavior is not happening in the router.match. Is there any reason behind it?
I have defined my router in entry module defined below and it will go to Layout component to look for route paths:
import { Router, Route, IndexRoute, hashHistory } from "react-router";
ReactDOM.render(
<Router history={hashHistory}>
<Route path="/" component={Layout}>
//<IndexRoute component={Featured}></IndexRoute>
<Route path="archives" name="archives" component={Archives}></Route>
<Route path="settings" component={Settings}></Route>
<Route path="featured" component={Featured}></Route>
</Route>
</Router>,
document.getElementById('app'));
Layout component will bind the path defined in router to different components in Layout Component
import { Link } from "react-router";
class Layout extends React.Component(
render(){
return(
{this.props.children}
<li><MenuItem><Link to="archives">archives</Link></MenuItem></li>
<li><MenuItem><Link to="settings">settings</Link></MenuItem></li>
<li><MenuItem><Link to="featured">featured</Link></MenuItem></li>
)
}
);
You can define any action for your route components.its working fine for me