I watched some stack overflow topic but they was always using function components. I want to know how can I generate custom url in TypeScript like http://localhost:3000/user/:userUid with Class Component.
I try this:
the path in Route is '/user/:userUID'
interface IURLInfo {
userUID: string
}
interface IProps extends RouteComponentProps<IURLInfo> {
}
interface IState {
}
class ShipsBoyBoatDashboard extends React.Component<IProps, IState> {
constructor(props: IProps) {
super(props)
}
componentDidMount() {
userIsLogged()
firebaseAuth().onAuthStateChanged(function(user) {
if(user) {
}
})
}
render() {
return(
<div className="ship-dashboard-container">
<p>{this.props.match.params.userUID}</p>
</div>
)
}
}
export default withRouter(ShipsBoyBoatDashboard)
But React send me back an error: TypeError: this.props.match is undefined
UPDATE
this is my index.tsx:
import './index.css';
import App from './App';
import { Router } from 'react-router-dom';
import history from './utils/history';
ReactDOM.render(
<React.StrictMode>
<Router history={history}>
<App />
</Router>
</React.StrictMode>,
document.getElementById('root')
);
And my App.tsx (even it is useless)
import React from 'react';
import { Switch, Route } from 'react-router-dom';
import BaseLayout from './utils/baseLayout';
import routes from './routes';
export default class extends React.Component {
state = {
showNavbar: true
}
showNavbar = (showNavbar = true) => {
this.setState({ showNavbar });
}
render() {
return (
<BaseLayout showNavbar={this.state.showNavbar}>
<Switch>
{routes.map(route => (
<Route
exact
key={route.path}
path={route.path}
render={() => (
<route.component
showNavbar={() => this.showNavbar(route.showNavbar)}
/>
)}
/>
))}
</Switch>
</BaseLayout>
);
}
}
The issue is your use of the render prop. See the documentation here.
The function you pass to render takes the react-router injected route props (i.e. the match prop and others) as an argument, but you then need to manually pass those props through to the component you're rendering in the function. That doesn't happen automatically.
This is unlike using the component prop where the route props are automatically injected into the component by react-router - that might be what you've seen in other examples.
Try this
render={(routeProps) => ( // routeProps is an argument to the render function
<route.component
showNavbar={() => this.showNavbar(route.showNavbar)}
{...routeProps} // you need to pass them through to the rendered component
/>
)}
Or, to demonstrate passing match more directly...
render={({ match }) => (
<route.component
showNavbar={() => this.showNavbar(route.showNavbar)}
match={match}
/>
)}
I have an App, a Navbar and a Content class, I am trying to pass a prop from navbar to content that will be rendered when I redirect to the content page.
App.js
import React, { Component } from 'react';
import { BrowserRouter as Router, Route } from 'react-router-dom';
import Navbar from './components/Home/Navbar';
import Content from './components/Home/Content';
export default class App extends Component {
render() {
return (
<Router>
<div>
<Navbar />
<Route path="/content" component={Content} render={(props) => <Navbar {...props} test={this.state.test} />} />
</div>
</Router>
);
}
}
Navbar.js
import React from 'react';
class Navbar extends React.Component {
constructor(props) {
super(props);
this.state = {
test: 'test',
}
}
render() {
return (
<div>
This is my navbar
</div>
);
}
}
export default Navbar;
Content.js
import React from 'react';
class Content extends React.Component {
constructor(props) {
super(props);
this.state = {
x: 'x',
test: this.props.test
}
}
render() {
return (
<div>
<p>{this.state.x}</p>
<p>{this.state.test}</p>
</div>
);
}
}
export default Content;
The problem that I am having is that is when I redirect to the content page, the state from the navbar class is not being passed through to it. How do I fix this?
The problem mostly lays in the fact that you are using both component and render props in your Route, you should use only one. If you do not want to change or pass along anything from where your Route is defined, you should use component property.
If you do wish to pass along some information at that point, use the render prop as you have done (however, I believe you really wanted to render the Content component and not the NavBar as in your OP)
<Route path="/content" render={(props) => <Content {...props} test={this.state.test} />} />
Then you really don't need any of your local state you were displaying, and content could be a functional component instead, like
const Content = ({ x, test }) => (
<>
<p>{ x }</p>
<p>{ test }</p>
</>);
where x and test would be destructured from your props, giving you easy access to it (you could also use (props) and then props.test and props.x depending on how you like to write it)
u can pass state like this with redirect :
<Redirect to={{
pathname: '/content',
state: { test: '123' }
}}
/>
and for accessing it :
this.props.location.state.test
I have these components. I want to turn every <House/> into a dynamic url. For example, when accessing in the browser, http://localhost:3000/houses/house/1
I want to appear the House 1.
The other things in the application are working fine. I just want to solve this problem of implementing dynamic routes.
Router Component
import React from 'react';
import { BrowserRouter as Router, Route, NavLink } from 'react-router-dom'
import App from './App'
import Houses from './Houses'
export default props => (
<Router>
<Route exact path='/' render={() => <App />} >
<Route exact path='/houses' render={() => <Houses />} />
</Route>
</Router>
)
Houses Component
import React, { Component } from 'react'
import House from './House'
var data = require('./db.json');
class Houses extends Component {
constructor(props) {
super(props);
this.state = {
currentHouse: []
};
}
componentDidMount() {
this.setState({
currentHouse: data[0]
})
}
render() {
const {currentHouse} = this.state;
return (
<div className="content house">
<ul>
{currentHouse.photos && currentHouse.photos.map((photo, index) => {
return(
<House photo={photo} key={index}/>
)
})}
</ul>
</div>
)
}
}
export default Houses
House Component
import React from 'react';
function House(prop) {
return (
<li><img src={`/images/${prop.photo}`}/></li>
);
}
export default House;
<Route exact path='/houses/:id' render={(props) => <House {...props} />} />
and inside House component retrieve the id:
prop.match.params.id
Ref: https://scotch.io/courses/using-react-router-4/route-params
const rootEl = document.getElementById('root');
ReactDOM.render(
<BrowserRouter>
<Switch>
<Route exact path="/">
<MasterPage />
</Route>
<Route exact path="/details/:id" >
<DetailsPage />
</Route>
</Switch>
</BrowserRouter>,
rootEl
);
I am trying access the id in the DetailsPage component but it is not being accessible. I tried
<DetailsPage foo={this.props}/>
to pass parameters to the DetailsPage, but in vain.
export default class DetailsPage extends Component {
constructor(props) {
super(props);
}
render() {
return (
<div className="page">
<Header />
<div id="mainContentContainer" >
</div>
</div>
);
}
}
So any idea how to pass the ID on to the DetailsPage ?
I used this to access the ID in my component:
<Route path="/details/:id" component={DetailsPage}/>
And in the detail component:
export default class DetailsPage extends Component {
render() {
return(
<div>
<h2>{this.props.match.params.id}</h2>
</div>
)
}
}
This will render any ID inside an h2, hope that helps someone.
If you want to pass props to a component inside a route, the simplest way is by utilizing the render, like this:
<Route exact path="/details/:id" render={(props) => <DetailsPage globalStore={globalStore} {...props} /> } />
You can access the props inside the DetailPage using:
this.props.match
this.props.globalStore
The {...props} is needed to pass the original Route's props, otherwise you will only get this.props.globalStore inside the DetailPage.
Since react-router v5.1 with hooks:
import { useParams } from 'react-router';
export default function DetailsPage() {
const { id } = useParams();
}
See https://reacttraining.com/blog/react-router-v5-1/
Use render method:
<Route exact path="/details/:id" render={(props) => (
<DetailsPage id={props.match.params.id}/>
)} />
And you should be able to access the id using:
this.props.id
Inside the DetailsPage component
In addition to Alexander Lunas answer ...
If you want to add more than one argument just use:
<Route path="/details/:id/:title" component={DetailsPage}/>
export default class DetailsPage extends Component {
render() {
return(
<div>
<h2>{this.props.match.params.id}</h2>
<h3>{this.props.match.params.title}</h3>
</div>
)
}
}
Use the component:
<Route exact path="/details/:id" component={DetailsPage} />
And you should be able to access the id using:
this.props.match.params.id
Inside the DetailsPage component
This is for react-router-dom v6 (I highly suggest using functional components for this)
It's somewhat painful for react-router-dom to keep changing syntax and rules. But here goes nothing.
You can use both useParams and useSelector to solve this
import { useParams } from 'react-router';
import { useSelector } from 'react-redux';
const Component = () => {
const { id } = useParams(); //returns the :id
const page = useSelector((state) => state.something[id]); //returns state of the page
return <div>Page Detail</div>;
}
export default Component;
BUT, the problem persist when you also have an action creator and you want to pass it as a props in connect function
export const connect(mapStateToProps, mapDispatchToProps)(Component)
since we are using useParams, it won't be passed to mapStateToProps that we created
const mapStateToProps = (state, ownProps) => {
console.log(ownProps) //wont recognize :id
//hence
return {
someReducers: state.someReducers[id] //would return an error: 'id' is not defined
};
};
on the other hand, you can't entirely ignore the connect function since you need mapDispatchToProps to work with your component.
The workaround to this is to create a Higher Order Component withRouter function yourself. This was a deprecated react-router-dom helper.
//make this
import { useParams, useLocation, useNavigate } from 'react-router';
import { connect } from 'react-redux';
import { yourActionCreator } from '../actionCreator';
const withRouter = (Child) => {
return (props) => {
const location = useLocation();
const navigation = useNavigate();
const params = useParams();
return (
<Child
{...props}
params={params}
navigate={navigate}
location={location}
/>
);
};
};
const Component = () => {
// your component...
return <div> Page Detail </div>
};
export mapStateToProps = (state, ownProps) => {
console.log(ownProps) // would contain the :id params
return {
//something
}
};
const mapDispatchToProps = {
yourActionCreator
}
export withRouter(connect(mapStateToProps, mapDispatchToProps)(Component));
Here's typescript version. works on "react-router-dom": "^4.3.1"
export const AppRouter: React.StatelessComponent = () => {
return (
<BrowserRouter>
<Switch>
<Route exact path="/problem/:problemId" render={props => <ProblemPage {...props.match.params} />} />
<Route path="/" exact component={App} />
</Switch>
</BrowserRouter>
);
};
and component
export class ProblemPage extends React.Component<ProblemRouteTokens> {
public render(): JSX.Element {
return <div>{this.props.problemId}</div>;
}
}
where ProblemRouteTokens
export interface ProblemRouteTokens {
problemId: string; }
Another solution is to use a state and lifecycle hooks in the routed component and a search statement in the to property of the <Link /> component. The search parameters can later be accessed via new URLSearchParams();
<Link
key={id}
to={{
pathname: this.props.match.url + '/' + foo,
search: '?foo=' + foo
}} />
<Route path="/details/:foo" component={DetailsPage}/>
export default class DetailsPage extends Component {
state = {
foo: ''
}
componentDidMount () {
this.parseQueryParams();
}
componentDidUpdate() {
this.parseQueryParams();
}
parseQueryParams () {
const query = new URLSearchParams(this.props.location.search);
for (let param of query.entries()) {
if (this.state.foo!== param[1]) {
this.setState({foo: param[1]});
}
}
}
render() {
return(
<div>
<h2>{this.state.foo}</h2>
</div>
)
}
}
FOR version 6 ( 2022 )
Note: using useParams you can easily get your params in your component.
look at the example below
import React from "react";
import { BrowserRouter as Router, Routes, Route, Link } from "react-router-dom";
import Home from "./compo/home";
import About from "./compo/about";
import Login from "./compo/login";
import "./styles.css";
const App = () => {
return (
<Router>
<div className="container">
<Link to="/home">Home</Link>
<Link to="/about">About</Link>
<Link to="/login">Login</Link>
</div>
<Routes>
<Route path="/home" element={<Home />} />
<Route path="/about" element={<About />} />
<Route path="/login" element={<Login />} />
<Route path="/login/:name" element={<Login />} />
</Routes>
</Router>
);
};
export default App;
Login Component
import { useParams } from "react-router-dom";
const Login = () => {
let { name } = useParams();
return <h1>i am {name ? <b>{name}</b> : "login"}</h1>;
};
export default Login;
if you are using class component, you are most likely to use GSerjo suggestion. Pass in the params via <Route> props to your target component:
exact path="/problem/:problemId" render={props => <ProblemPage {...props.match.params} />}
In the latest version of (react-router-dom#6.3.0), you can do it like this:
<Route path="path" element={<YourComponent type="simple" />} />
Here, type is the input passed to YourComponent
I was working on react-router-dom version 6.3.0 and above solution didn't resolve my problem. Then I use something like this and it worked:
<Route exact path='/payment-status/:userId/:orderId' element={<PaymentStatus/>}/>
And on PaymentStatus.js page I did like this:
import { useParams } from 'react-router-dom'
export const PaymentStatus = () => {
let {userId, orderId}=useParams()
return (
<div>
<h2>order ID : {orderId}</h2>
<h2>user ID : {userId}</h2>
</div>
)
}
It worked for me. I hope it may help someone. Thanks!
try this.
<Route exact path="/details/:id" render={(props)=>{return(
<DetailsPage id={props.match.params.id}/>)
}} />
In details page try this...
this.props.id
Simple example with Class, HoC and Router v5
package.json
"react-router-dom": "5.3.1",
"react-router": "5.3.1",
"#types/react-router-dom": "5.3.3",
// YourComponent.tsx
import React from 'react';
import { RouteComponentProps, withRouter } from 'react-router';
export interface PathParams {
id: string;
}
export interface Props extends RouteComponentProps<PathParams> {}
export interface State {}
class YourComponent extends React.Component<Props, State> {
constructor(props: Props) {
super(props);
this.state = {};
console.log(props.match.params) // { id: 1 }
// TypeScript completions
console.log(props.match.params.id) // 1
}
render() {
return <></>;
}
}
export default withRouter(YourComponent);
// App.tsx
import './App.css';
import React from 'react';
import { Route, Switch, Router } from 'react-router-dom';
import YourComponent from './YourComponent';
function App(): JSX.Element {
return (
<Router>
<Switch>
<Route
path="/details/:id"
component={() => <YourComponent />}
/>
</Switch>
</Router>
);
}
export default App;
I have a state 'isLoggedIn' in App Component.
Now, I want to pass this state as props to the child component 'Secret Component'.
<BrowserRouter>
<App>
<Switch>
<Route path='/secret' component={Secret} />
<Route path='/' component={Top} />
</Switch>
</App>
</BrowserRouter>
But, I'm using react-router(ver4.1) like this and can't figure out how to pass the state of App Component as props to its child component.
const childrenWithProps = React.Children.map(this.props.children, (child) => {
console.log(child);
}
);
I know, by doing like this, I can get an access to this.props.children and set additional props to them but since I wrap my components with Router Component, the children of App Component are now Route components, which makes it complicated...
Could anyone please tell me how to do it?
I'm also worried if I'm doing wrong on how to use react-router.
thanks!
index.js(entry point)
import React, { Component } from 'react';
import ReactDOM from 'react-dom';
import { BrowserRouter, Route, Switch } from 'react-router-dom';
import App from './components/App';
import Secret from './components/Secret';
import Top from './components/Top';
ReactDOM.render(
<BrowserRouter>
<App>
<Switch>
<Route path='/secret' component={Secret} />
<Route path='/' component={Top} />
</Switch>
</App>
</BrowserRouter>
,
document.querySelector('.container')
);
App.js
import React, { Component } from 'react';
import NavigationMenu from './NavigationMenu';
export default class App extends Component {
constructor(props) {
super(props);
this.state = {
isLoggedIn: false
};
this.toggleAuthenticationStatus = this.toggleAuthenticationStatus.bind(this);
}
toggleAuthenticationStatus() {
this.setState({
isLoggedIn: !this.state.isLoggedIn
});
}
render() {
//I want to pass this.state.isLoggedIn as props to Secret Component!!!
const childrenWithProps = React.Children.map(this.props.children, (child) => {
console.log(child);
}
);
return (
<div>
<NavigationMenu isLoggedIn={this.state.isLoggedIn} toggleAuthenticationStatus={this.toggleAuthenticationStatus} />
{this.props.children}
</div>
)
}
}
Secret.js
import React, { Component } from 'react';
class Secret extends Component {
constructor(props) {
super(props);
}
componentWillMount() {
if (this.props.isLoggedIn === false) {
this.props.history.push('/');
}
}
componentWillUpdate() {
if (this.props.isLoggedIn === false) {
this.props.history.push('/');
}
}
render() {
return (
<div>
This content is only for our members!
</div>
)
}
}
export default Secret;
In react-router v4 recommended approach is putting nested routes inside the parent component instead of pass those as children (see the basic example of react-router v4). So in your case, I suggest you to simply replace {this.props.children} with Routes with the Switch component and stop passing them as the children of App. Then you can use render method of Route to pass props to the Secret component as usual.
return (
<div>
<NavigationMenu isLoggedIn={this.state.isLoggedIn} toggleAuthenticationStatus={this.toggleAuthenticationStatus} />
<Switch>
<Route path='/secret' render={() => <Secret isLoggedIn={this.state.isLoggedIn}/>)} />
<Route path='/' component={Top} />
</Switch>
</div>
)