The RouteParamPage.tsx is a React component that can consume URL parameters via the router. It is working.
The call is like this:
<Route path={"/p2/:lastname"} component={RouteParamPage}/>
The ParamPage.tsx component has a parameter in the signature.
I can call these components without a router like this:
<ParamPage label={"with params V2"}/>
The question is, how can I call the ParamPage.tsx component in the Route?
That doesn't work:
<Route path={"/p1"} component={ParamPage label={"with params V2"}}/>
Does somebody has any idea?
----------------- RouteParamPage.tsx --------------
import React from 'react';
import { useParams } from 'react-router-dom';
interface RouteParams {
lastname: string
}
export default function RouteParamPage() {
const params = useParams<RouteParams>();
return (
<React.Fragment>
<h1>{params.lastname}</h1>
</React.Fragment>
)
}
----------------- ParamPage.tsx --------------
import React from 'react';
type childProps = {
label: string,
}
export default function ParamPage( data: childProps ) {
return (
<React.Fragment>
<h1>{ data.label }</h1>
</React.Fragment>
)
}
----------------- Main.tsx --------------
import React from 'react';
import {BrowserRouter as Router, Switch, Route} from "react-router-dom";
import Home from "./Home";
import ParamPage from "./ParamPage";
import RouteParamPage from "./RouteParamPage";
export default function Main() {
return (
<React.Fragment>
<ParamPage label={"with params V2"}/> {/*<<<<this works*/}
<Router>
<Switch>
<Route path={"/"} exact component={Home}/>
<Route path={"/p1"} component={ParamPage label={"with params V2"}}/> {/*<<< That doesn't work:*/}
<Route path={"/p2/:lastname"} component={RouteParamPage}/>
</Switch>
</Router>
</React.Fragment>
)
}
You should use render props to pass parameter to the component which is render by the Route component like this
<Route path="/p1" render={(routeProps) => {
return <ParamPage label={"with params V2"}} {...routeProps}/>
}} />
To learn more about check the React Render Props Documentation
For this case, there is a render prop which needs to be used instead of component.
The code will be modified to below
<Route
path={"/p1"}
render={(props) => <ParamPage {...props} label={"with params V2"} />}
/>
Hope it helps. Revert for any doubts/confusions.
Related
I am trying to separate react lazy in 2 separate modules , due to the fact that I want to make my private route take multi components not only one.
my current app.tsx module looks like the following :
import React, { useState, useEffect, useContext, lazy, Suspense } from 'react';
import { BrowserRouter as Router, Route, Switch, HashRouter, Redirect } from 'react-router-dom';
// Privite Route Component
import { PrivateRoute } from './components/routes/PrivateRoute';
import { NotFound } from './components/routes/NotFound';
// Components **********************************/
import Home from './components/Home/Home';
import { AuthAction } from './components/routes/AuthAction';
// Context API
import AuthProvider from './context/auth/AuthState';
// Lazy
const AppMain = lazy(() => import('././components/App/CentralHub'));
function App() {
return (
<Router>
<AuthProvider>
<Switch>
<Route exact path='/' component={Home} />
<PrivateRoute exact path='/app' component={AppMain} />
/>
<PrivateRoute exact path='/auth/action' component={AuthAction} />
<Route path='*' component={NotFound} />
</Switch>
</AuthProvider>
</Router>
);
}
export default App;
Would calling react suspense inside of a private route that receives a that target component as propose considered a bad idea? my private rout receives this component, as props from the app.tsx now the suspense action runs from the privet route, yet my component is getting imported still from out side form (the app.tsx) should I rather split private routes and lazy import the props component inside of the private route rather than from out side ?
or would my current model considered clean regardless of the fact that lazy is working in other component than suspense dose ?
import React, { useContext, useEffect, Suspense } from 'react';
import { Redirect, Route, useHistory, useLocation } from 'react-router-dom';
import { useAppSelector } from '../../app/hooks';
interface Props {
component: React.FC<any>;
exact?: boolean;
path: string;
}
export const PrivateRoute: React.FC<Props> = ({ component: Component, ...rest }) => {
const location = useLocation();
const authenticating = useAppSelector((state) => state.auth.authenticating);
const currentUser = useAppSelector((state) => state.auth.currentUser);
return (
<>
{location.pathname.startsWith('/app') && !authenticating && (
<Route
{...rest}
render={(props) =>
!currentUser?.authenticated /*Route Condtion*/ ? (
<Redirect to='/' />
) : (
<Suspense fallback={<div></div> /*null*/}>
<Component {...props} />
</Suspense>
)
}
/>
)}
</>
);
};
I'm currently trying to get the id params for a route, but it keeps turning up as 'undefined' when I console log it.
Here's where the route is defined. For some reason when I console.log(id), it keeps giving 'undefined'. Even when I put it in the body, it doesn't show up.
return (
<div className="App">
<Header />
<BrowserRouter>
<Routes>
<Route path="/monster/:id" element={<Monster />} />
<Route path="/monsterCard" element={<MonsterCard />} />
<Route path="/index" element={<Homepage />} />
</Routes>
</BrowserRouter>
</div>
);
And here's the component where I want to get the Id parameter
import React, { Component, useEffect, useState } from "react";
import { withRouter, useParams } from "react-router";
import "./Monster.css";
export default function Monster() {
const { id } = useParams();
console.log(id);
return <div className="monster-image-frame"></div>;
}
You are trying to import useParams from react-router but instead of it just try to import it from "react-router-dom".
import {useParams} from "react-router-dom";
Is there a way to pass the location prop and own made prop to another component? I've figured out how to pass DIR_URL through a function like below but I also need to use location prop later in ConfirmAccount component to read pathname property and so on. (Of course in this way it gets true value).
import React, { Component, Fragment } from 'react';
import { BrowserRouter as Router, Switch, Route } from 'react-router-dom';
import Main from './components/structure/Main';
import ConfirmAccount from './components/pages/ConfirmAccount';
import NoMatch from './components/pages/NoMatch';
class App extends Component {
render() {
const url = 'http://localhost:3006';
return (
<Fragment>
<Router>
<Switch>
<Route exact path="/" component={Main} />
<Route path="/confirm">
{/* How can I pass the location? */}
<Route path="/:url" component={() => <ConfirmAccount DIR_URL={url} location />} />
</Route>
<Route component={NoMatch} />
</Switch>
</Router>
</Fragment>
);
}
}
export default App;
React Router DOM automatically passes match location and history props.
You can use the route render prop to pass them manually if you wish:
<Route path="/:url" render={(routeProps) => <ConfirmAccount DIR_URL={url} {...routeProps} />} />
I suggest that you use useHistory hook from ReactRouterDom inside your child component. There you got all the location stuff that you need.
Or pass route properties to rendering component:
import React, { Component, Fragment } from 'react';
import { BrowserRouter as Router, Switch, Route, useHistory } from 'react-router-dom';
import Main from './components/structure/Main';
import ConfirmAccount from './components/pages/ConfirmAccount';
import NoMatch from './components/pages/NoMatch';
class App extends Component {
render() {
const url = 'http://localhost:3006';
return (
<Fragment>
<Router>
<Switch>
<Route exact path="/" component={Main} />
<Route path="/confirm">
{/* How can I pass the location? */}
<Route path="/:url" component={(routeProps) => <ConfirmAccount DIR_URL={url} {...routeProps} />} />
</Route>
<Route component={NoMatch} />
</Switch>
</Router>
</Fragment>
);
}
}
const ConfirmAccount = ({location}) => {
const history = useHistory()
}
export default App;
just import { useLocation } from 'react-router-dom' and use it like this:
const location = useLocation()
now you can access the location object.
read more about it here: https://reactrouter.com/web/api/Hooks/uselocation
or you can use withRouter HOC like this https://reactrouter.com/web/api/withRouter
In a normal react project my router would look like this
I would have my app wrapped in a Component , and then I would have this
<Switch>
<Route path="/login" render={(props) => <LoginPage login={this.login} authed={this.state.isAuthenticated} {...props} />} />
<Route path="/" render={(props) => this.props.history.push("/login")} />
</Switch>
But I dont know how to simulate something similar in typescript . I currently have this
const App: React.FunctionComponent = () => {
return (
<Router>
<div className="main-content">
<div>
<Switch>
<Route exactly component={Main} exact pattern="/" />
<Route exactly component={Count} exact pattern="/count" />
</Switch>
</div>
</div>
</Router>
);
};
But it always redirects to main component. How could I do that an undefined route like /randomroute , would redirect to "/" , and how would I make the alternative route /count work?
EDIT: I have progressed a bit
import React from "react";
import { BrowserRouter as Router, Route, Switch, RouteComponentProps, RouteProps } from "react-router-dom";
import Main from "./components/Main";
import Count from "./components/Count"
import "./App.css";
interface ChildComponentProps extends RouteProps {
/* other props for ChildComponent */
}
const App: React.FunctionComponent<ChildComponentProps> = (props) => {
return (
<Router>
<div className="main-content">
<div>
<Switch>
<Route exactly component={Main} exact path="/" />
<Route exactly component={Count} exact path="/count" />
<Route pattern ="/" render={() => props.history.push("/") } />
</Switch>
</div>
</div>
</Router>
);
};
export default App;
Problem now is that, history is inside the RouteComponentProps interface, while RouteProps interface contains render, and I cant use them at the same time, so im a bit lost
EDIT2: Trying this
interface RenderProps extends RouteProps {
/* other props for ChildComponent */
}
interface HistoryProps extends RouteComponentProps {
}
const App: React.FunctionComponent<HistoryProps & RenderProps>
Receiving in render
The expected type comes from property 'render' which is declared here
on type 'IntrinsicAttributes &
IntrinsicClassAttributes<Route> & Readonly &
Readonly<...>'
When supposedly that interface is imported
Edit4:
I did this
import React from "react";
import { BrowserRouter as Router, Route, Switch, RouteComponentProps, RouteProps, withRouter } from "react-router-dom";
import Main from "./components/Main";
import Count from "./components/Count"
import "./App.css";
interface RenderProps extends RouteProps {
/* other props for ChildComponent */
}
interface HistoryProps extends RouteComponentProps {
}
const App: React.FunctionComponent<RenderProps & HistoryProps> = (props) => {
return (
<div className="main-content">
<div>
<Switch>
<Route exactly component={Main} exact path="/" />
<Route exactly component={Count} exact path="/count" />
<Route path ="/" {...props.history.push("/")} />
</Switch>
</div>
</div>
);
};
export default withRouter(App);
And wrapper the app component in index.tsx file into (browserouter)
Now im getting a
Error: Maximum update depth exceeded. This can happen when a component
repeatedly calls setState inside componentWillUpdate or
componentDidUpdate. React limits the number of nested updates to
prevent infinite loops
Because of the props.history.push
I ended up doing this, but im waiting for better answers
import React from "react";
import { Route, Switch, RouteComponentProps, withRouter} from "react-router-dom";
import Main from "./components/Main";
import Count from "./components/Count";
import Redirector from "./components/Redirect"
import "./App.css";
interface HistoryProps extends RouteComponentProps {
}
const App: React.FunctionComponent<HistoryProps> = (props) => {
return (
<div className="main-content">
<div>
<Switch>
<Route exactly component={Main} exact path="/" />
<Route exactly component={Count} exact path="/count" />
<Route path ="/" exactly component={Redirector}/>
</Switch>
</div>
</div>
);
};
export default withRouter(App);
Redirect.tsx
import * as React from "react";
import { Redirect } from "react-router-dom";
const Redirector: React.FunctionComponent = () => {
return <Redirect to='/'/>;
};
export default Redirector
EDIT2: Probably a better approach
import React from "react";
import { Route, Switch, withRouter, Redirect} from "react-router-dom";
import Main from "./components/Main";
import Count from "./components/Count";
import "./App.css";
const App: React.FunctionComponent = () => {
const renderFor404Routes = () => (<Redirect to='/'/>);
return (
<div className="main-content">
<div>
<Switch>
<Route exactly component={Main} exact path="/" />
<Route exactly component={Count} exact path="/count" />
<Route path ="/" exactly component={renderFor404Routes}/>
</Switch>
</div>
</div>
);
};
export default withRouter(App);
I am new in ReactJs.
I need a route like localhost:3000/directory/category/region/brandName and for the same route, I need to render a component
Sample of URL be like
localhost:3000/directory/photography/france/testA
localhost:3000/directory/Catering/germany/testB
for both above URLs, a component called name.js should render
You can make use of react-router and then configure your Routes by making use of Route params
import { BrowserRouter as Router, Route, Switch } from 'react-router-dom';
const App () => {
return (
<Router>
<Switch>
<Route path="/directory/:profession/:country/:value" component={Name} />
<Route path="/" component={Dashboard}/>
</Switch>
)
}
Now post this you can access the params in name component and fetchData from api or have any other logic
const Name = () => {
const { profession, country, value} = useParams();
useEffect(() => {
// Any fetch logic based on params
}, [profession, country, value]);
return (
..
)
}
You can read more about react-router usage here and also refer the docs
As far as I understand from the question, you can handle this through using "Redirect" component. Let there be a "Navigation" component where the "Router" is defined as you did
Navigation.js
import Name from './name';
import From from './from';
<Router>
<Switch>
<Route path="/from">
<From />
</Route>
<Route path="/directory/:profession/:country/:value">
<Name />
</Route>
</Switch>
</Router>
and a "From" component where paths and redirections are defined. If "redirectionPath" is not null you can return "Redirect" component in render. Thus, you can redirect to and render the Name component.
From.js
import React, {Component} from 'react';
import {
Redirect
} from "react-router-dom";
class From extends Component {
state={
redirectionPath: "/directory/photography/france/testA" // or setState anywhere you need.
}
...
render(){
if(this.state.path){
return (<Redirect to={this.state.redirectionPath} />)
}
return (
<SomeComponent/>
);
}
}
This can be one of the solutions. Hope it works for you as well.