I have a app created from create-react-app and the prop location for all my components is always undefined..
Here is index.js
ReactDOM.render((
<BrowserRouter >
<App/>
</BrowserRouter>
), document.getElementById('root'));
registerServiceWorker();
Here is a short version of app.js
import {BrowserRouter as Router, Route, Switch} from 'react-router-dom';
class App extends React.Component {
constructor(props) {
super(props);
this.state = {
auth: UserProfile.getAuth(),
}
}
render() {
if(this.state.auth){
return (
<Router>
<Switch>
...
<Route path="/editUser" render={()=><EditUser className="App" app={this}/>} />
</Switch>
</Router>
);
}
else{
...
}
}
}
now here is editUser.jsx
class EditUser extends React.Component {
constructor(props) {
super(props);
...
}
componentDidMount(){
console.log(this.props.location) //undefined
}
render() {
return (<div>...</div>)
I keep getting undefined and I dont understand why...
I use "react-router-dom": "^4.3.1" according to my package.json
Please help!
You need to pass the router props into the component since you're using the render prop of the <Route/> component.
Also, In your App component you don't need BrowserRouter as Router since you're already wrapping <App/> in index.js. I changed the import and removed the wrapping <Router/> component.
import { Route, Switch } from 'react-router-dom';
class App extends React.Component {
...
render() {
if (this.state.auth) {
return (
<Switch>
...
<Route path="/editUser" render={props =>
<EditUser {...props} className="App" app={this}/>
}/>
</Switch>
);
}
}
}
I think you need to use the withRouter HOC on your EditUser Component in order to inject props like location.
See react-router getting this.props.location in child components
Related
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 am attempting to pass data via react-router-dom, specifically I wanted to hold state data in the App.js file which I am using to route to different pages. I can't get the props to pass. What am I doing off here? Below is an example of what I am trying to do:
App.js
import React, { Component } from 'react';
import Home from './Home';
import {BrowserRouter as Router, Route,Switch, withRouter } from 'react-router-dom';
class App extends Component {
constructor(props) {
super(props);
this.state = {
testProps:7
}
}
render() {
return (
<Router>
<div>
<Route
exact path="/"
component = {Home}
render={(props) => <Home testProps={this.state.testProps} {...props} />}/>
</div>
</Router>
);
}
}
export default App;
Home.js
import React, { Component } from 'react';
class Home extends Component {
render() {
return (
<div>
{`passing props from state: ${this.props.testProps}`}
</div>
);
}
}
export default Home;
In my home page I see: passing props from state: undefined. Am I approaching this incorrectly?
I have a react component which is loaded on routing
I need to access a parameter from the url inside the components constructor
How do I do it?
can I access it like this:
class CustomCoponent extends React.Component {
constructor(props,{match}) {
}
}
You can access route parameter in react-router-dom v4.x, by getting params from the match props.
Where you define your routes,
import { BrowserRouter as Router, Switch, Route, Link } from 'react-router-dom';
...
<Router>
<App>
<Switch>
<Route exact path="/" component={List} />
<Route path="/:parameterToAccess" component={CustomComponent} />
</Switch>
</App>
</Router>
...
In your component,
class CustomComponent extends React.Component {
constructor(props) {
super(props);
this.routeParam = props.match.params.parameterToAccess;
}
}
if you use routing then you can specify your route to expect the parameter.
<Route path='/yourpath/:ParamName' component={CustomComponent}/>
your component needs to be wrapped in the withRouter HOC for you to access this.
import {withRouter} from 'react-router-dom';
class CustomComponent extends React.Component{
constructor(props){
}
//**ACCESS PARAMETER VALUE LIKE THIS**
sample(){
let Paramvalue=this.props.match.params.ParamName;
}
}
export default withRouter(CustomComponent);
You can do it like this:
class CustomComponent extends React.Component {
constructor({ match, ...props }) {
console.log(match.params)
}
}
As {match} is passed to the component as a prop(property) so we can access this prop as a normal prop way.
class CustomComponent extends React.Component {
console.log(this.props.match.url)
}
Routes
import logo from './logo.svg';
import './App.css';
import {BrowserRouter as Router,Route,Switch} from 'react-router-dom';
import UpdateEmployeeComponent from './components/UpdateEmployeeComponent';
function App() {
return (
<div>
<Router>
<div className="container">
<Switch>
<Route path="/update-employee/:id" component={UpdateEmployeeComponent}></Route>
</Switch>
</div>
</Router>
</div>
);
}
export default App;
Component
import React, { Component } from 'react';
class UpdateEmployeeComponent extends Component {
constructor(props){
super(props);
this.state ={
id : this.props.match.params.id
}
console.log('Employee Id ::: '+this.id);
}
render() {
return (
<div>
</div>
);
}
}
export default UpdateEmployeeComponent;
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>
)
I'm trying to pass a dynamic state to all the routes in a React router, specifically a shopping cart (an array of objects).
The layout is I have a parent component which contains the router and all the routes, and in that I want to store the cart in state and pass it to the routes (so essentially all routes will have access to it). I've been trying a few different things and troubleshooting it by looking it up on forums for a while but I just can't get it. This is the latest setup I have:
- Main.jsx
// This is the app entry point
import React, { Component } from 'react';
import { render } from 'react-dom';
import RouterHub from './RouterHub.jsx';
render((
<RouterHub />
), document.getElementById('root'));
- RouterHub.jsx
import React, { Component } from 'react';
import { render } from 'react-dom';
import { Router, Route, hashHistory } from 'react-router'
import Home from './Home.jsx';
import Dogs from './Pages/Other.jsx';
class RouterHub extends Component {
constructor(props) {
super(props);
this.addItem = this.addItem.bind(this);
this.state = {
cart: []
};
}
addItem(item) {
let newCart = this.state.cart.splice();
newCart.push(item);
this.setState({cart: newCart});
}
render() {
return(
<Router history={hashHistory}>
<Route path="/" component={Home} cart={this.state.cart} addItem={this.addItem} />
<Route path="/other" component={Other} cart={this.state.cart} addItem={this.addItem}/>
</Router>
);
}
}
export default RouterHub;
- Home.jsx
import React, { Component } from 'react';
import Slideshow from './Home/Slideshow.jsx';
import Navbar from './Constants/Navbar.jsx';
import Footer from './Constants/Footer.jsx';
class Home extends Component {
constructor(props) {
super(props);
}
render() {
return(
<div>
<button onClick={() => this.props.route.addItem('potato')}>click me</button>
<Navbar />
// All the JSX content, I've removed to make it succint
<Footer />
</div>
);
}
}
export default Home;
Essentially what I'm wanting is in Home.jsx, when I click that button, I want another potato added to the cart. However, with this setup I get the error:
bundle.js:46451 Warning: [react-router] You cannot change <Router routes>; it will be ignored
How do I get it so that updating state in the RouterHub passes that to the routes, or is that not possible and I'm doing this all the wrong way?
Thanks for any help
Since you already have a main component for holding your state, you should insert that in the top level Route component something like this:
render((
<Router history={browserHistory}>
<Route path="/" component={RouterHub}>
<Route path="home" component={Home}/>
</Route>
</Router>
), document.getElementById('root'))
Then in your RouterHub component, pass those clone each children components with props, something like this:
{
React.Children.map( this.props.children, (child) => {
return React.cloneElement(child, this.props)
})
}
Bumping into this kind of problems will make you think of using some state management libraries like Redux/Flux.