Attempting to pass a custom attribute into react router (using v4) - javascript

I have the following render method in my App.js:
render() {
return (
<LocaleProvider locale={enUS}>
<Router history={history}>
<div>
<Header />
<Switch>
<Route exact path={home} component={requireAuth(Groups)} />
<Route exact path={groups} component={requireAuth(Groups)} />
<Route path={addcard} component={requireAuth(AddCard)} />
<Route path={db} component={requireAuth(DbTbl)} />
<Route component={NoMatch} />
</Switch>
</div>
</Router>
</LocaleProvider>
);
};
And I want to pass in a custom property to the first 2 routes ({home} & {groups}) so I'm attempting to do something like this:
render() {
const groups1 = () => <Groups studyState={this.toggleStudyState}/>;
return (
<LocaleProvider locale={enUS}>
<Router history={history}>
<div>
<Header />
<Switch>
<Route exact path={home} render={requireAuth(groups1)} />
<Route exact path={groups} render={requireAuth(groups1)} />
<Route path={addcard} component={requireAuth(AddCard)} />
<Route path={db} component={requireAuth(DbTbl)} />
<Route component={NoMatch} />
</Switch>
</div>
</Router>
</LocaleProvider>
);
};
But I'm getting this error message from react router:
connectAdvanced.js:3 Uncaught TypeError: Cannot call a class as a
function
So I suspect react router doesn't like the high order component syntax with the render= attribute. How can I pass the studyState attribute into the Groups component being used with the first two tags?

The render property must have a function passed to it. You are giving a Class.
Write something like that:
<Route exact path={home} render={(routerProps) => {
const AuthGroups = requireAuth(Groups);
return <AuthGroups studyState={this.toggleStudyState}/>;
}} />
As I don't know how the requireAuth works, I don't know if the studyState property will be properly passed to the Group component. But I guess it will.
Now to have a cleaner code (if the above solution works):
renderAuthGroups(routerProps) {
const AuthGroups = requireAuth(Groups);
return <AuthGroups studyState={this.toggleStudyState}/>;
}
render() {
return (
<LocaleProvider locale={enUS}>
<Router history={history}>
<div>
<Header />
<Switch>
<Route exact path={home} render={this.renderAuthGroups.bind(this)} />
<Route exact path={groups} render={this.renderAuthGroups.bind(this)} />
<Route path={addcard} component={requireAuth(AddCard)} />
<Route path={db} component={requireAuth(DbTbl)} />
<Route component={NoMatch} />
</Switch>
</div>
</Router>
</LocaleProvider>
);
};

Related

Redirect to specific page if localstorage value not found in react

I am using reactjs.
I have multiple following routes in my index.js file
<BrowserRouter>
<App>
<Switch>
<Route exact path="/" component={Home} />
<Route exact path="/Login" component={SignIn} />
<Route exact path="/Sign-up" component={SignUp} />
<Route exact path="/Orders" component={Orders} />
<Route exact path="/Category" component={Category} />
<Route exact path="/Shops" component={Shops} />
</Switch>
</App>
</BrowserRouter>
initially when user goes to base URL suppose
Http://localhost:3000
he should be redirected to
Http://localhost:3000/Shops page if value of localstorage item is null
and also if user tries to visit other pages he should be redirected to the /Shops page.
One way of doing this is using HOC but further i'll be adding auth soo there i'll have to wrap the component in route with HOC like this
<Route exact path="/Orders" component={AuthGuard(Orders)} />
I dont know whether I can do like this
<Route exact path="/Orders" component={AuthGuard, ShopGuard(Orders)} />
soo how can i achieve this without using HOC or how can I wrap 2 HOC for a single component.
Thanks.
function HandleRedirection() {
const RedirectToShop = ({ component: Component, ...rest }) => {
return (
<Route
{...rest}
render={(props) =>
localStorage.getItem('user') ? (
<App>
<Component {...props} />
</App>
) : (
<Redirect to="/shop" />
)}
/>
);
};
return (
<BrowserRouter basename={`/`}>
<Switch>
<Route path={`/shop`} component={Shops} />
<RedirectToShop exact path={`/login`} component={Signin} />
<RedirectToShop exact path={`/order`} component={Order} />
<RedirectToShop exact path={`/category`} component={Category} />
<Redirect to="/shop" />
</Switch>
</BrowserRouter>
);
}
Create a custom Route component that can check localStorage and redirect to "/shop" if condition is (or isn't?) met.
const ShopGuardRoute = ({ component: Component, ...props }) => (
<Route
{...props}
render={routeProps => {
const item = localStorage.getItem("key");
// Do all your conditional tests here
return item !== null ? (
<Component {...routeProps} />
) : (
<Redirect to="/shop" />
);
}}
/>
);
Usage
<BrowserRouter>
<App>
<Switch>
<ShopGuardRoute path="/Login" component={SignIn} />
<ShopGuardRoute path="/Sign-up" component={SignUp} />
<ShopGuardRoute path="/Orders" component={Orders} />
<ShopGuardRoute path="/Category" component={Category} />
<Route path="/Shops" component={Shops} />
<Route path="/" component={Home} />
</Switch>
</App>
</BrowserRouter>
If you plan on adding an authentication check then auth-workflow may help.
I think you can just do something like this on all the required pages if(condition // your local storage null check) { history.push(/yourPath, dataIfAny); }

React nested routing problem (cannot displayed the child component)

I am new to react development. And I want to implement the routing mechanism in my page.
For example, there's component contains routes with the <Home /> and <Login /> component.
function App() {
return (
<div className="App">
<Switch>
<Route exact path="/home">
<Home />
</Route>
<Route path="/login">
<Login />
</Route>
</Switch>
</div>
);
}
The <Home /> component contains a <Navbar /> and a <Switch /> with two <Route />:
Home.js
function Home() {
return (
<div>
<Navbar />
<div>
<Switch>
<Route exact path={`/home`}>
<Menu />
</Route>
<Route path={`/home/temperature`}>
<div>temperature</div>
</Route>
</Switch>
</div>
</div>
)
}
However, I defined the <Link /> in the <Menu /> component as below:
function Menu() {
return (
<div>
<li>
<Link to={`/home/temperature`}>temperature child page</Link>
</li>
</div>
)
}
Originally, the page would displayed the <Home /> component with <Menu /> and <div> temperature </div>
I expected that when I clicked the link (<Link to={/home/temperature}>temperature child page</Link>) then it would replace the <Menu /> component with the only the <div>temperature</div> (Dispalyed the <Navbar/> and <div>temperature</div>, but it could not display anything.
How should I correct it?
Solution:
I finally figure out why I cannot get child component in my js script.
Firstly, I need to wrap the <Switch> with <Router> in <App> component.
Then, by reference this , I realized that I should not specify the exact in <Route path="/home"> to make sure that the nested route can work as well.
function App() {
return (
<div className="App">
<Router>
<div>
<Switch>
<Route path="/home">
<Home />
</Route>
<Route path="/login">
<Login />
</Route>
</Switch>
</div>
</Router>
</div>
);
}
simple routing
<Router>
<Switch>
<Route path={"/home"} exact component={Home} />
</Switch>
</Router>
nested routing
<Router>
<Switch>
<Route path={"/home"} exact component={Home}
<Rout path={"/temperature"} exact component={Temperature} />
</Route>
</Switch>
</Router>
`

How to set up nested routes in react-redux application?

I am trying to set up a login page for my app, but when I try to redirect using this.props.history.push the new page does not render. My app uses redux which wraps my main file AsyncApp with Provider. AsyncApp has all my routes wrapped with various navigation bars that appear on every page. Now I am trying to do a login page but I don't know how to implement its route in my application since its route does not use the navigation bars therefore it will not reside in AsyncApp. I dont want to rename all my existing pages because the login page is the only page that does use the navigation bars.
I have tried making a component APP that is wrapped my the provider and has a route for the login page and the other routes. This isn't working.
Root.js
const store = configureStore()
export default class Root extends Component {
render() {
return (
<Provider store={store}>
<App />
</Provider>
)
}
}
App.js
export default class App extends Component {
render() {
let arr = window.location.pathname.split('/');
let loc = arr[1];
if(loc === 'signin'){
return (
<Router>
<Route exact path="/signin" component={SignIn} />
</Router>
)
} else {
return (
<AsyncApp />
)
}
}
}
AsyncApp.js
class AsyncApp extends Component {
render() {
const { classes } = this.props
return (
<ThemeProvider theme={theme}>
<div className={classes.root}>
<CssBaseline />
<nav className={classes.drawer}>
<Hidden xsDown implementation="css">
<Navigator PaperProps={{ style: { width: drawerWidth } }} />
</Hidden>
</nav>
<div className={classes.appContent}>
<Header onDrawerToggle={this.handleDrawerToggle} />
<main className={classes.mainContent}>
<div>
<Router>
<Route exact path="/EditContracts/:contractId/sections/:section" component={EditSection} />
<Route exact path="/EditContracts/:contractId" component={EditContract} />
<Route exact path="/EditUsers/:userId" component={EditUser} />
<Route exact path="/EditEndpoints/:epId" component={EditEndpoint} />
<Route exact path="/EditContracts/:contractId/addSection" component={CreateSection} />
<Route exact path="/Contracts/List" component={Contracts} />
<Route exact path="/Contracts/Create" component={CreateContract} />
<Route exact path="/Contracts/Import" component={ImportContract} />
<Route exact path="/Users/List" component={Users} />
<Route exact path="/Users/Create" component={CreateUser} />
<Route exact path="/Endpoints/Create" component={CreateEndpoint} />
<Route exact path="/Endpoints/List" component={Endpoints} />
</Router>
</div>
</main>
</div>
</div>
</ThemeProvider>
)
}
}
I expect to be able to keep AsyncApp how it is while being able to have a login page that can redirect to any page on AsyncApp.
1) Wrap the entire app around a router so you don't have to have multiple routers set up:
export default class Root extends Component {
render() {
return (
<Provider store={store}>
<BrowserRouter>
<App />
</BrowserRouter>
</Provider>
)
}
}
2) Utilize the Switch function to route your pages
export default class App extends Component {
render() {
<Switch>
<Route exact path="/signin" component={SignIn} />
<Route path="/" component={AsyncApp} />
</Switch>
}
}
class AsyncApp extends Component {
...
<Switch>
<Route exact path="/EditContracts/:contractId/sections/:section" component={EditSection} />
<Route exact path="/EditContracts/:contractId" component={EditContract} />
<Route exact path="/EditUsers/:userId" component={EditUser} />
<Route exact path="/EditEndpoints/:epId" component={EditEndpoint} />
<Route exact path="/EditContracts/:contractId/addSection" component={CreateSection} />
<Route exact path="/Contracts/List" component={Contracts} />
<Route exact path="/Contracts/Create" component={CreateContract} />
<Route exact path="/Contracts/Import" component={ImportContract} />
<Route exact path="/Users/List" component={Users} />
<Route exact path="/Users/Create" component={CreateUser} />
<Route exact path="/Endpoints/Create" component={CreateEndpoint} />
<Route exact path="/Endpoints/List" component={Endpoints} />
</Switch>
...
3) In your SignIn component add a state variable called redirect that you set to true if you are signed in. Then
if (redirect) {
return <Redirect to="path/to/redirect" />
}
This will set up your routes and allow you to do your redirects w/out manipulating the window and refreshing the app

react-router: functions are not valid react child

I noticed a lot of questions has been asked about functions not valid as react child but none fits my case from what I saw.
I use react-router and the error (Functions are not valid as a React child. This may happen if you return a Component instead of from render. Or maybe you meant to call this function rather than return it.)
occurs when I try to use the Es6 class syntax to create my App components.
Here is my code:
import {BrowserRouter as Router, Route, Switch} from 'react-router-dom';
//all other imports are here too
class App extends Component {
render () {
return (
<Router>
<div className="App">
<Header />
<Switch>
<Route path ='/blog/' component={Blog} />
<Route path ='/about/' component={About} />
<Route path ='/register/' component={Register} />
<Route component={Carousel} />
</Switch>
<Route path='/' exact component={Main} />
<Route path='/foreign/' component={Foreign} />
<Route path='/local/' component={Local} />
<Route path='/snacks/' component={Snacks} />
</div>
</Router>
);
}
}
if I change the Es6 class syntax to a function like this,
const App = (
<Router>
<div className="App">
<Header />
<Switch>
<Route path ='/blog/' component={Blog} />
<Route path ='/about/' component={About} />
<Route path ='/register/' component={Register} />
<Route component={Carousel} />
</Switch>
<Route path='/' exact component={Main} />
<Route path='/foreign/' component={Foreign} />
<Route path='/local/' component={Local} />
<Route path='/snacks/' component={Snacks} />
</div>
</Router>
);
It works perfectly. I don't know why this is happening
following Garret Motzner comment I switched the Dom render function from this
ReactDOM.render(App, document.getElementById('root'));
to
ReactDOM.render(<App />, document.getElementById('root'));
and it now work

Render multiple components in React Router

I'm used to application layouts with multiple yield areas, i.e. for content area and for top bar title. I'd like to achieve something similar in React Router. For example:
<Router>
<Route path="/" component = { AppLayout }>
<Route path="list"
component = { ListView }
topBarComponent = { ListTopBar }/>
</Route>
</Router>
AppLayout:
<div className="appLayout box">
<div className="appLayout topBar">
{ -- display ListTopBar here -- }
</div>
<div className="appLayout content">
{ -- display ListView here -- }
</div>
</div>
Both child components should receive the same props.
How can I approach this?
To passe multiple component you can do like this :
<Route path="groups" components={{main: Groups, sidebar: GroupsSidebar}} />
<Route path="users" components={{main: Users, sidebar: UsersSidebar}}>
See the doc here :
https://github.com/ReactTraining/react-router/blob/v3/docs/API.md#named-components
In v4, according to the docs, you can render multiple components like this:
<Route path='/some-path' render={() =>
<Fragment>
<FirstChild />
<SecondChild />
</Fragment>
} />
Instead of using div's you can use Fragments.
`
<Route path='/some-path' render={props =>
<Fragment>
<Child 1/>
<Child 2/>
</Fragment>
} />
`
You can also use Array in latest versions of React-router-dom;
<Route path="groups" element={[<Component1/>,<Component2/>]} />
Will work just fine.
To render multiple components you can do this:
<Route
path="/EditEmployee/:id"
render={(props) =>
<div>
<NavMenu />
<EditEmployee {...props} />
</div>
}
/>
Here I'm passing parameter to specific conponent.
//this is the simplest method to render multiple components and it works for me
<Router>
<Route path="/">
<ListView />
<ListTopBar />
</Route>
</Router>
<Route path='/' element={<><Header /> <Home /></>} />
This worked for me in the latest react router dom v6
Another method is within the render method of route multiple passed components can be created using react.createElement
<Route render ={(props)=>React.createElement(Component1, {...props}},
React.createElement(Component2, {...props}}/>
What worked for me was to wrap the multiple components in a <Fragment> or a <div> as a parent element.
return (
< Router>
<div className="App" >
<Routes>
<Route path='/'
element={
<Fragment>
< NavBar />
< NewsLetterCard />
< TestimonialsCard />
< ServicesCard />
< ContactsCard />
< Footer />
</Fragment>
}
/>
</Routes>
</div>
</Router>
);
}
For v6, where you are using Routes instead of Switch to render your components. This works:
<Router>
<Routes>
<Route path='/' element={<><Child1/> <Child2/></>} />
</Routes>
</Router>
But for v5, this works:
<Router>
<Switch>
<Route path="/">
<Child1/>
<Child2/>
</Route>
This seem to work for me, I needed to add canvas animation component as background under homepage ("/") only:
import Page from 'pages/Page';
import Blog from 'pages/Blog';
import Post from 'pages/Post';
import Category from 'pages/Category';
import CanvasParticles from 'components/canvas/CanvasParticles';
...
<Routes>
{['/', '/:slug'].map((path, index) => {
return path === '/' ? (
<Route
exact
path={path}
element={[<CanvasParticles />, <Page />]}
key={index}
/>
):(
<Route path={path} element={<Page />} key={index} />
);
})}
<Route exact path="/blog" element={<Blog />}></Route>
<Route path="/blog/:slug" element={<Post />}></Route>
<Route path="/category/:slug" element={<Category />}></Route>
</Routes>
click on this you can view a image v6 feature : this is the simplest method to render multiple components and it works for me
Main Concept is to you should wrap with element
<Route path="/" element={<> </>}
or
wrap with in Fragment
<Route path="/" element={ }

Categories