I have problem with Router in React, after login i change type state in Redux from 0 to 1, then i make switch in my App file, but i got error
Warning: [react-router] You cannot change <Router routes>; it will be ignored
This is my index.js, I want change all Route links if user is login (form with login work good and they change redux state type to 1):
#connect((store)=>{
console.log(store)
return {
typeUser: store.app.type
}
})
class App extends React.Component{
render(){
switch(this.props.typeUser){
case 0:{
return(
<Router history={browserHistory}>
<Route path={"/"} component={MainPage}></Route>
<Route path={"/login"} component={Login}></Route>
<Route path={"product/:nameProduct/:id"} component={ProductDetails}></Route>
</Router>
)
break;
}
case 1:{
return(
<Router history={browserHistory}>
<Route path={"/"} component={MainPageAfterLogin}></Route>
<Route path={"/login"} component={LoginAfterLogin}></Route>
</Router>
)
break;
}
}
}
}
const app = document.getElementById('app');
ReactDOM.render(<Provider store={store}>
<App/>
</Provider>,app);
You cannot change the Router but you can change the Routes configuration that you have , so you can setup the Routes like
class App extends React.Component{
render(){
return(
<Router history={browserHistory}>
{this.props.typeUser === 0? <User1/>: <User2/>}
</Router>
)
}
}
class User1 extends React.Component {
render() {
return (
<div>
<Route path={"/"} component={MainPage}></Route>
<Route path={"/login"} component={Login}></Route>
<Route path={"product/:nameProduct/:id"} component={ProductDetails}></Route>
</div>
)
}
}
class User2 extends React.Component {
render() {
return (
<div>
<Route path={"/"} component={MainPage}></Route>
<Route path={"/login"} component={Login}></Route>
</div>
)
}
}
Related
I have a lot of files, but I think the problem is coming from my authentication component in React. I basically want to only display a page if the user is logged in otherwise I want to the user to be redirected.
react-dom.development.js:14887 Uncaught Error: Objects are not valid as a React child (found: object with keys {$$typeof, type, compare, WrappedComponent}). If you meant to render a collection of children, use an array instead.
requireAuth.js
// function that can wrap any component to determine if it is authenticated
import React, { Component } from "react";
import { connect } from "react-redux";
import PropTypes from "prop-types";
import { push } from "#lagunovsky/redux-react-router"; // not sure if this correct
export default function requireAuth(Component) {
class AuthenticationComponent extends React.Component {
constructor(props) {
super(props);
this.checkAuth();
}
componentDidUpdate(prevProps, prevState) {
this.checkAuth();
}
checkAuth() {
// if not authenticated then it is redirected
if (!this.props.isAuthenticated) {
const redirectAfterLogin = this.props.location.pathname;
this.props.dispatch(push(`/login?next=${redirectAfterLogin}`));
}
}
// if authenticated then renders the component
render() {
return (
<div>
{this.props.isAuthenticated === true ? (
<Component {...this.props} />
) : null}
</div>
);
}
}
AuthenticationComponent.propTypes = {
isAuthenticated: PropTypes.bool.isRequired,
location: PropTypes.shape({
pathname: PropTypes.string.isRequired,
}).isRequired,
dispatch: PropTypes.func.isRequired,
};
// checks isAuthenticated from the auth store
const mapStateToProps = (state) => {
return {
isAuthenticated: state.auth.isAuthenticated,
token: state.auth.token,
};
};
return connect(mapStateToProps)(AuthenticationComponent);
}
App.js
class App extends Component {
render() {
return (
<div>
<Root>
<ToastContainer hideProgressBar={true} newestOnTop={true} />
<Routes>
<Route exact path="/" element={<Home />} />
<Route path="/closet" element={requireAuth(Closet)} />
<Route path="*" element={<NotFound />} />
</Routes>
</Root>
</div>
);
}
}
I have done some digging but I can't find a problem like this.
The error is because on this line:
<Route path="/closet" element={React.createComponent(requireAuth(Closet))} />
You're passing the actual class definition to the element prop and not an instance of the class (which would be the React component). To fix this, you can use React.createElement:
class App extends Component {
render() {
return (
<div>
<Root>
<ToastContainer hideProgressBar={true} newestOnTop={true} />
<Routes>
<Route exact path="/" element={<Home />} />
<Route path="/closet" element={React.createElement(requireAuth(Closet))} />
<Route path="*" element={<NotFound />} />
</Routes>
</Root>
</div>
);
}
}
Because Route's element props need a ReactNode,But requireAuth(Closet)'s type is () => React.ReactNode, you can change your App.js like this:
const AuthComponent = requireAuth(Closet);
class App extends Component {
render() {
return (
<div>
<Root>
<ToastContainer hideProgressBar={true} newestOnTop={true} />
<Routes>
<Route exact path="/" element={<Home />} />
<Route path="/closet" element={<AuthComponent />} />
<Route path="*" element={<NotFound />} />
</Routes>
</Root>
</div>
);
}
}
I'm trying to redirect to an error page I created whenever the user puts an invalid path on my application. Let's say the user puts domain.com/efneroguqf or any random path.
Here's my App function inside App.js
function App() {
return (
<div className="App">
<BrowserRouter>
<Suspense fallback={(<div>Loading</div>)}>
<Navbar />
<Route exact path="/" component={Home} />
<Route path="/SignUp" component={SignUp} />
<PrivateRoute path="/Publish" component={Publish} />
<Route path="/AdminGenerator" component={AdminGenerator} />
<Route path="/AdminUsers" component={AdminUsers} />
<Route path="/List" component={List} />
<Route path="/publication" component={Details} />
<Route path="/error" component={ErrorBoundary} />
<PrivateRoute path="/MyPublications" component={MyPublications} />
<PrivateRoute path="/MyFavorites" component={MyFavorites} />
<PrivateRoute path="/MyInformation" component={MyInformation} />
<PrivateRoute path="/EditPublication" component={EditPublication} />
<Route exact path="*" component={Home} />
</Suspense>
</BrowserRouter>
</div>
);
}
Now I want to redirect to /error in case the path is invalid like I mentioned above. How can I achieve that?
Update: Here's my error page. I want to set the coding to 404 when this happens.
class ErrorBoundary extends React.Component {
constructor(props) {
super(props);
this.state = {
coding: props.location.state.coding
};
}
render(){
const { t } = this.props;
var codeMsg = t('errors.errorCode') + this.state.coding
return (
<div>
<div id="error-container">
<h1 id="error-title">{t('errors.errorTitle')}</h1>
<p id="error-status">{codeMsg}</p>
<p id="error-message">{t('errors.errorMessage')}</p>
</div>
<div id="link2-container">
{t('errors.errorBackHome')}
</div>
</div>
);
}
}
As the very last path, use a wildcard..
// last path in route list
<Route path="*" component={NotFoundPage} />
With 404 page:
const { Component } = React;
const { render } = ReactDOM;
const { Switch, Route, Link, HashRouter, withRouter } = ReactRouterDOM;
class Header extends Component {
render() {
return (
<div>
<ul>
<li><Link to='/'>Home</Link></li>
<li><Link to='/about'>About</Link></li>
<li><Link to='/contact'>Contact</Link></li>
<li><Link to='/iDontReallyExist'>Simulate Non-existent route</Link></li>
</ul>
</div>
);
}
}
class Main extends Component {
render() {
return (
<main>
<Switch>
<Route exact path='/' component={HomePage}/>
<Route path='/about' component={AboutPage}/>
<Route path='/contact' component={ContactPage}/>
<Route path='*' component={FourZeroFour}/>
</Switch>
</main>
);
}
}
class HomePage extends Component {
render() {
return (
<div>Home Page</div>
);
}
}
class AboutPage extends Component {
render() {
return (
<div>About Page</div>
);
}
}
class ContactPage extends Component {
render() {
return (
<div>Contact Page</div>
);
}
}
class SomeNonExistentRoute extends Component {
render() {
return (
<div>I dont exist</div>
);
}
}
class FourZeroFour extends Component {
render() {
return (
<div>Uh oh can't find that! 404!</div>
);
}
}
class Application extends Component {
render() {
let curRoute = this.props.location.pathname
return (
<div>
<Header />
<p>Current Route: {curRoute}</p>
<Main />
</div>
);
}
}
const App = withRouter(Application);
render((
<HashRouter>
<App />
</HashRouter>
), document.body);
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/16.12.0/umd/react.production.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/16.11.0/umd/react-dom.production.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-router-dom/5.1.2/react-router-dom.min.js"></script>
With Home page as '404':
const { Component } = React;
const { render } = ReactDOM;
const { Switch, Route, Link, HashRouter, withRouter } = ReactRouterDOM;
class Header extends Component {
render() {
return (
<div>
<ul>
<li><Link to='/'>Home</Link></li>
<li><Link to='/about'>About</Link></li>
<li><Link to='/contact'>Contact</Link></li>
<li><Link to='/iDontReallyExist'>Simulate Non-existent route</Link></li>
</ul>
</div>
);
}
}
class Main extends Component {
render() {
return (
<main>
<Switch>
<Route exact path='/' component={HomePage}/>
<Route path='/about' component={AboutPage}/>
<Route path='/contact' component={ContactPage}/>
<Route path='*' component={HomePage}/>
</Switch>
</main>
);
}
}
class HomePage extends Component {
render() {
return (
<div>Home Page</div>
);
}
}
class AboutPage extends Component {
render() {
return (
<div>About Page</div>
);
}
}
class ContactPage extends Component {
render() {
return (
<div>Contact Page</div>
);
}
}
class SomeNonExistentRoute extends Component {
render() {
return (
<div>I dont exist</div>
);
}
}
class FourZeroFour extends Component {
render() {
return (
<div>Uh oh can't find that! 404!</div>
);
}
}
class Application extends Component {
render() {
let curRoute = this.props.location.pathname
return (
<div>
<Header />
<p>Current Route: {curRoute}</p>
<Main />
</div>
);
}
}
const App = withRouter(Application);
render((
<HashRouter>
<App />
</HashRouter>
), document.body);
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/16.12.0/umd/react.production.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/16.11.0/umd/react-dom.production.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-router-dom/5.1.2/react-router-dom.min.js"></script>
<Switch>
...
<Route component={Error} />
<Switch />
If you render a Route but don’t specify a path prop, that route will always be rendered.
If you are using React v6+, instead of the component we have to use element in Route.
And you can specify the error page functional component as shown below:
import React, { Component } from 'react';
import './App.css';
import Login from './component/Login';
import Register from './component/Register';
import Home from './component/Home'
import { BrowserRouter, Route, Routes } from 'react-router-dom';
class App extends Component {
render() {
return (
<div className="App">
<BrowserRouter>
<Routes>
<Route path="/" element={<Login/>}></Route>
<Route path="/register" element={<Register/>}></Route>
<Route path="/home" element={<Home/>}></Route>
<Route path="*" element={<ErrorComponent/>}></Route>
</Routes>
</BrowserRouter>
</div>
);
}
}
function ErrorComponent(){
return <div>Some error occured. Invalid page</div>
}
export default App;
I am relatively new to react and attempting to create an app that has 2 designs. One is the public site that has a common header and footer and the internal app which has an admin header and side bar. I created a Router and 2 main routes '/' and '/app'. I then added subroutes hoping that if the parent routers were matched, it would show the parent component and pass the sub route's component as the this.props.children. I seemed to have done it wrong. Here is what I created.
App.js:
...
class App extends Component {
render() {
return (
<Router>
<Switch>
<Route component={Public}>
<Route exact path="/" component={Home}/>
<Route path="/login" component={Login}/>
</Route>
<Route component={Main}>
<Route path="/dash" component={Dash}/>
<Route path="/people" component={People}/>
</Route>
</Switch>
</Router>
);
}
}
...
Public 'template':
...
render(){
return(
<div>
I am a public page
{this.props.children}
</div>
)
}
...
Home.js
...
class Home extends Component{
render(){
return(
<div>I am the home page</div>
)
}
}
...
App 'template'
...
class Main extends Component{
render(){
return(
<div>
<Header />
<div>I am an app page</div>
{this.props.children}
<Footer/>
</div>
)
}
}
...
Dash.js
...
class Dash extends Component{
render(){
return(
<div>I am the dash page</div>
)
}
}
...
Thanks and if any one can tell me or point me to a good resource I would appreciate it!
You're really close! What you need to do is actually put the template components as the parent not in a <Route /> component. A components children are the components in between its start and end tag. Keep on trucking !
class App extends Component {
render() {
return (
<Router>
<Switch>
<Public>
<Route exact path="/" component={Home}/>
<Route path="/login" component={Login}/>
</Public >
<Main>
<Route path="/dash" component={Dash}/>
<Route path="/people" component={People}/>
</Main>
</Switch>
</Router>
);
}
}
I am trying to pass a value from the render function to the component:
= react_component('App', props: {test: 'abc'}, prerender: false)
Routes.jsx
<Route path="/" component={App} >
App.jsx (component)
class App extends React.Component {
render() {
return (
<Header test={this.props.test}>
</Header>
{this.props.children}
<Footer />
);
}
}
App.propTypes = { test: PropTypes.string };
There does not seem to be a coherent answer to this complete flow.
I have tried the following:
<Route path="/" component={() => (<App myProp="value" />)}/>
But this still does not answer the question of picking up the value provided by the initial render call(react_component)
Looking for an end to end answer on how to pass a parameter from the
"view" to the "react router" to the "component"
We will start from the view:
<%= react_component('MyRoute', {test: 123}, prerender: false) %>
Now we will create a component that holds our route:
class MyRoute extends Component{
constructor(props){
super(props)
}
render(){
return(
<Switch>
<Route path="/" render={() => <App test={this.props.test} />} />
<Route path="/login" component={Login} />
</Switch>
)
}
}
As you can see, we passed the test prop from the Route component to the App component. Now we can use the test prop in the App component:
class App extends Component{
constructor(props){
super(props)
}
render(){
return(
<h1>{this.props.test}</h1>
)
}
}
<Route path="/" render={attr => <App {...attr} test="abc" />} />
In Router v3 you would do something like this
Wrap your App component under withRouter like this
import { withRouter } from 'react-router';
class App extends React.Component {
render() {
return (
<Header test={this.props.test}>
</Header>
{
this.props.children &&
React.clone(this.props.children, {...this.props} )}
<Footer />
);
}
}
App.propTypes = { test: PropTypes.string };
export const APP = withRouter(App);
And construct your routes like this...
<Route path="/" component={APP}>
<Route path="/lobby" component={Lobby} />
<Route path="/map" component={GameMap} />
...
</Route>
So your child routes will be rendered inside the APP children property an the props will be passed down to them.
Hope this helps!
All of my main react components have some parts like this:
export default class ExampleMain extends Component {
constructor(props) {
super(props)
this.state = {
isAuthenticated: Meteor.userId() !== null
}
}
componentWillMount() {
if (!this.state.isAuthenticated) browserHistory.push('/login')
}
componentDidUpdate() {
if (!this.state.isAuthenticated) browserHistory.push('/login')
}
}
With this I am checking if a user is logged in. If this is false, the user will be redirected to login route.
As this part is used in many components, I was thinking if I can optimize this to get a DRY code...
Update
I am using react router:
render((
<Router history={ browserHistory }>
<Route path='/' component={ App }>
<IndexRoute component={ Main } />
<Route path='login' component={ Login } />
<Route path='content/:id' component={ Content } />
</Route>
<Redirect from='*' to='/' />
</Router>
), document.getElementById('root'))
You can try something like this:
<Router history={ browserHistory }>
<Route path='/' component={ App }>
<IndexRoute component={ Main } />
<Route path='/login' component={ Login } />
<Route path='content/:id' component={ Content } />
</Route>
<Redirect from='*' to='/' />
</Router>
And in App, using withRouter to "inject" the router inside your component:
import { withRouter } from 'react-router';
class App extends React.Component {
constructor(props) {
super(props);
this.state = {
isAuthenticated: Meteor.userId() !== null
}
}
componentWillMount() {
if (!this.state.isAuthenticated) {
this.props.router.push('/login');
}
}
}
export default withRouter(App);
Maybe this helps you. I would tried to use any hook before routing. But you always can extend your own class with such functionality like that example
function requireAuth(nextState, replace) {
if (!auth.loggedIn()) {
replace({
pathname: '/login',
state: { nextPathname: nextState.location.pathname }
})
}
}
render((
<Router history={ browserHistory }>
<Route path='/' component={ App }>
<IndexRoute component={ Main } />
<Route path='login' component={ Login } />
<Route path='content/:id' component={ Content } onEnter={requireAuth} />
</Route>
<Redirect from='*' to='/' />
</Router>
), document.getElementById('root'))
To see full code follow link above.