React. New Router after login - javascript

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

Why am I getting "Objects are not valid as a React child" error?

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>
);
}
}

Redirecting to error page when path is invalid

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;

React Router V4 Routers with Master Pages / Templates

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>
);
}
}

ReactJS: Pass parameter from rails to react router to component

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!

React: Authentification and avoiding repeating code components

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.

Categories

Resources