How to use url params in separate component - javascript

I've been learning React over the last 2 days, and I'm having trouble with understanding URL parameters.
Let's say I want a route mysite.com/details/1023. The route is defined like:
<Route path="/details/:id" render={() => <DishDetails />}/>
Now I want the DishDetails object, which is defined in another file, to be able to use the id value 1023. How can I do this? I've found tutorials on route url params but none that explains how to achieve this.
Here's what my DishDetails view looks like right now:
import React, {Component} from "react";
import "./DishDetails.css";
import {Link} from "react-router-dom";
class DishDetails extends Component {
constructor(props) {
super(props);
this.state = {
id: /*url param*/,
};
}
render() {
return this.state.id;
}
}
export default DishDetails;
Where can I get the id in DishDetails? I've tried:
import React, {Component} from "react";
import "./DishDetails.css";
import {Link} from "react-router-dom";
class DishDetails extends Component {
constructor(props) {
super(props);
this.state = {
id: match.id,
};
}
render() {
return this.state.id;
}
}
But match is undefined.

Pass your component to Route via the component props:
<Route path="/details/:id" component={DishDetails} />
If you did this match is available in the props.
If you have to keep the way how you render your routes you can pass the render props manually to your component:
<Route path="/details/:id" render={(props) => <DishDetails {...props} />}/>
You can find the whole documentation for react-router here.

The <Route render> prop receives the router props:
match
location
history
You need to provide that props to the <DishDetails> component and use the match.params.id to retrieve the id from your path="/details/:id"
const DishDetails = props => <div>Details Id: {props.match.params.id}</div>;
<Route path="/details/:id" render={props => <DishDetails {...props} />} />
This is the Route props in your example:
{
match: { path: '/details/:id', url: '/details/1', isExact: true, params: { id: '1' } },
location: { pathname: '/details/1', search: '', hash: '' },
history: { length: 3, action: 'POP', location: { pathname: '/details/1', search: '', hash: '' } }
}
There are 3 ways to render something with a <Route>:
<Route component>
<Route render>
<Route children>
Read more here

Have you tried the withRouter function that comes with react-router?
import { withRouter } from 'react-router'
class App extends Component {
.... your stuff
}
export default withRouter(App);
That should give you access to the "match" prop

Related

React Authentication using HOC

The server sends a 401 response if the user is not authenticated and I was trying to check for authentication in the front end using a HOC as seen in Performing Authentication on Routes with react-router-v4.
However, I am getting an error saying TypeError: Cannot read property 'Component' of undefined in RequireAuth
RequireAuth.js
import {React} from 'react'
import {Redirect} from 'react-router-dom'
const RequireAuth = (Component) => {
return class Apps extends React.Component {
state = {
isAuthenticated: false,
isLoading: true
}
async componentDidMount() {
const url = '/getinfo'
const json = await fetch(url, {method: 'GET'})
if (json.status !== 401)
this.setState({isAuthenticated: true, isLoading: false})
else
console.log('not auth!')
}
render() {
const { isAuthenticated, isLoading } = this.state;
if(isLoading) {
return <div>Loading...</div>
}
if(!isAuthenticated) {
return <Redirect to="/" />
}
return <Component {...this.props} />
}
}
}
export { RequireAuth }
App.js
import React from 'react';
import { BrowserRouter as Router, Route, Switch, withRouter } from 'react-router-dom';
import SignIn from './SignIn'
import NavigationBar from './NavigationBar'
import LandingPage from './LandingPage'
import Profile from './Profile'
import Register from './Register'
import { RequireAuth } from './RequireAuth'
class App extends React.Component {
constructor(props) {
super(props);
}
render() {
return (
<div>
<Router>
<NavigationBar />
<Switch>
<Route exact path = '/'
component = {LandingPage}
/>
<Route exact path = '/register'
component = {Register}
/>
<Route exact path = '/profile'
component = {RequireAuth(Profile)}
/>
<Route path="*" component = {() => "404 NOT FOUND"}/>
</Switch>
</Router>
</div>
);
}
}
export default withRouter(App);
I can think of some possibilities:
------- Moved this to top which eventually fixed OP's issue -------
Try remove the curly braces at {React},
import React from 'react';
------- Moved this to top which eventually fixed OP's issue -------
In RequireAuth.js, Try
const RequireAuth = ({ Component }) => {} // changed from Component to { Component }
In App.js, use Component start with capital letter
<Route exact path = '/' Component = {LandingPage}/>
Also, in <Route path="*" component = {() => "404 NOT FOUND"}/>, looks like you're not passing in a React component because the function is not returning a JSX (I can't test now so I'm not very sure, though).
try this instead:
() => <div>404 NOT FOUND</div>
or if it doesn't work, define a functional component externally and pass into the Route:
const NotFoundComponent = () => <div>404 NOT FOUND</div>
<Route path="*" component = {NotFoundComponent}/>
try to do it like this:
const RequireAuth = ({ component: Component }) => {}

Passing Arguments to a React Element in a React Router

I have created a Element called Jobscard that takes jobsList as an argument. I am also trying to set this jobsCard as a Router using the react-router.
I have looked into the problem and their seems to be no similar problem on the web, all solutions and guides use react components but this doesn't work for react Elements. Is this possible?
import React from 'react';
import { projectList } from './projectList';
import JobsCard from './JobsCard';
import { jobsList } from './jobsList';
import CardList from './CardList';
import Ham from './hamburger';
import { Route, Switch } from 'react-router-dom';
import { BrowserRouter } from 'react-router-dom';
import jobsList from './jobsList'
const App = () => {
return(
<div className='blue1 center'>
<BrowserRouter>
<Ham />
<Switch>
<Route exact path="/" render{props => <JobsCard jobsList={jobsList}> </JobsCard> }/>
<Route path="/projects" component={<CardList projectList={projectList}/>}/>
</Switch>
</BrowserRouter>
</div>
)
}
export default App;
The JobsCard Element is
import React from 'react';
import Job from './Job.js'
const JobsCard = ({jobsList}) => {
const cardDraw = jobsList.map((proj, i) => {
return <Job key={i} title={jobsList[i].title}
website={jobsList[i].website} description={jobsList[i].description}
logo={jobsList[i].logo} company={jobsList[i].company} years={jobsList[i].years}/>
})
return (
<div className=" cf justify-center ma3">
{cardDraw}
</div>
);
}
export default JobsCard;
jobsList looks like this
import energyxchain from ''
export const jobsList = [
{ title: '',
website: '',
description: '',
logo: ,
company: '',
years: '2018-2019'
},
{
title: '',
company: '',
website: '',
logo: '',
description: '',
years: '2017-2018'
}];
I would like the jobsCard to be a route.
From route rendering props:
render, which takes an inline function, should only be used when you have to pass in-scope variables to the component you want to render.
You should not use the component prop with an inline function to pass in-scope variables because you will get undesired component unmounts/remounts.
So your route should be either the ff:
Using component prop (should not pass variable):
<Route
exact path="/"
component={JobsCard}
/>
Using render prop:
<Route
exact path="/"
render={routeProps => (<JobsCard {...routeProps} jobsList={jobsList} />)}
/>
Normally, you should separate navigation from the internal data management (using redux connect).
Change const JobsCard to function JobsCard
If you are planning to use a constant, it shouldn't have any arguments.
you can do
const paragraph = <p>Hello World!</p>
and render inside a react component using {paragraph}

Match the route with params by react-router v3

Have a couple of routes with params (like. /user/:user, car/:car/:year)
I'm trying to avoid to manually parse location.pathname if it's possible to use react-router (v3) for it.
How can I find the route that match to the current url location.
Need something similar to:
if (router.match_to_route('/user/:user')) {
... do something
}
...
The method matchRoutes in https://github.com/ReactTraining/react-router/blob/v3/modules/matchRoutes.js might be the one that I need.
Thanks.
Updated:
It can be achieved by
import { match } from 'react-router';
const routes = router.routes;
match({ routes, location }, (error, redirect, renderProps) => {
const listOfMatchingRoutes = renderProps.routes;
if (listOfMatchingRoutes.some(route => route.path === '/user/:user')) {
...
}
}
https://github.com/ReactTraining/react-router/issues/3312#issuecomment-299450079
I have done this using react-router-dom. I simplified the code so that you can easily understand it. I just passed the param user with my dashboard route in main App component and access it using this.props.match.params.user in my child component named as Dashboard.
App.js file
class App extends React.Component {
constructor(props) {
super(props);
this.state = {open: false};
this.state = {message: "StackOverflow"};
}
render(){
return (
<Router>
<div>
<Route exact path="/dashboard/:user" render={props => <Dashboard {...props} />} />
<Route exact path="/information" render={props => <Information {...props} />} />
</div>
</Router>
);
}
}
Dashboard.js
import React from 'react';
class Dashboard extends React.Component {
constructor(props) {
super(props);
}
render() {
return (
<div ><h1> Hello {this.props.match.params.user}</h1></div>
);
}
}
export default Dashboard;
I hope it will help you.
On RR4 you can use matchPath
const match = routes.find(route) => matchPath(props.location.pathname, {
path: route.path,
exact: true,
strict: false
})

ReactJS propagate data between Components

I am working on an SPA with ReactJS. I have a root component App and then several child components. In the the App component I am trying to store some application level state such as logged in user id, and other data. However I am not seeing my state be propagated down the child components.
App
import { Router, Route, Link, IndexRoute, browserHistory, hashHistory } from 'react-router';
import ParameterContainer from './components/parameter/parameter-container';
import NavMenu from './components/navigation/nav-menu';
import {Alert} from 'react-bootstrap';
import SelectFilter from './components/sample/sample-container';
// Main component and root component
export default class App extends React.Component {
constructor(props) {
super(props);
this.state = {
userId: null,
roles: null,
parameterTypes: {
'STRING': 'STRING',
'BOOLEAN': 'BOOLEAN',
'INTEGER': 'INTEGER',
'DECIMAL': 'DECIMAL'
}
};
}
render() {
return (
<div>
<NavMenu />
<div className="container">
{this.props.children}
</div>
</div>
)
}
}
// page for 404
class NoMatch extends React.Component {
render() {
return (
<div className="container">
<Alert bsStyle="danger">
<h1>404: Not Found</h1>
<h3>The requested resource does not exist!</h3>
</Alert>
<img src="images/404.png" style={{display: 'block', margin: '0 auto', width: 300, height: '*'}} />
</div>
)
}
}
// render the application
ReactDOM.render((
<Router history={hashHistory}>
<Route path="/" component={App}>
<Route path="parameter" component={ParameterContainer} />
<Route path="sample" component={SelectFilter} />
<Route path="*" component={NoMatch}/>
</Route>
</Router>
), document.getElementById('react'))
Child Component
import React from 'react';
export default class ParameterContainer extends React.Component {
constructor(props) {
super(props);
this.state = { parameters: [] };
this.client = rest.wrap(mime);
this.fetchFromApi = this.fetchFromApi.bind(this);
console.log('Props:' + props);
}
render() {
....
}
The this.props does not contain what I expected. I need to pass data down to children components.
State is not propagated to child components - you have to set props on child components to pass down data. You can use React.cloneElement to add properties to children:
let childrenWithProps = React.cloneElement({this.props.children, {
userid: this.state.userId,
...
});
The best way would be to use Redux to manage application data and store application level state in Redux store. If you don't use Redux you can also consider using react Context. According to docs you can use Context if:
you want to pass data through the component tree without having to
pass the props down manually at every level
To pass the state or props to the child component you can explicit them on your Route node
<Route path="parameter" component={ParameterContainer} parentState={this.state} />
You can, then, access them in the child component as props
constructor(props) {
super(props)
console.log('parentState:' + props.parentState);
}
Here are better and more detailed answers: react-router - pass props to handler component

Routes don't have access to store given in Provider Redux React

I would like my components to mostly have access to the store via their props.
I believe the correct way to do this when using redux is by using connect on the Provider
I have all of this set up, but still my Components do not have state as a prop
App.js
I have added everything here for completeness as I believe I am stupid , I am sorry.
import React, { Component, PropTypes } from 'react'
import { bindActionCreators } from 'redux'
import { connect } from 'react-redux'
import * as currentSexActionCreator from '../actions/currentSexActionCreator';
import { browserHistory } from 'react-router'
class App extends Component {
constructor(props) {
super(props)
this.handleChange = this.handleChange.bind(this)
}
handleChange(nextValue) {
browserHistory.push(`/${nextValue}`)
}
render() {
return (
<div>
{this.props.children}
</div>
)
}
}
App.propTypes = {
// Injected by React Router
children: PropTypes.node
}
function mapStateToProps(state, ownProps) {
return {
errorMessage: state.errorMessage,
inputValue: ownProps.location.pathname.substring(1),
'hutber':'hutber'
}
}
function mapDispatchToProps(dispatch) {
return {
currentSexAction: bindActionCreators(currentSexActionCreator, dispatch)
}
}
export default connect(mapStateToProps, mapDispatchToProps)(App)
A Component - That should have props
class Sex extends React.Component {
constructor(props) {
super(props);
this.state = {
participants: 1
}
}
onSetParticipants = (data) => {
store.dispatch (currentSex.setParticipants(data.valueText)); //the incorrect way to dispatch things, store is referencing the store
}
render() {
console.info(this.props);
return (
<div>
....
something here
....
</div>
)
}
}
export default Sex;
*The provider**
export default class Root extends Component {
render() {
const { store, history } = this.props;
return (
<Provider store={store}>
<Router history={history}>
<Route path="/" component={App}>
<IndexRoute component={Splash}/>
{/*No Signed In Page*/}
<Route path="/signin" component={SignIn} onEnter={authStates.blockForLoggedInUsers}/>
<Route path="/signup" component={signUp} onEnter={authStates.blockForLoggedInUsers}/>
<Route path="/signupconfirm" component={SignUpConfirm} onEnter={authStates.blockForLoggedInUsers}/>
{/*Login Required*/}
<Route path="/home" component={Home} onEnter={authStates.requireLogin}/>
<Route path="/selection" component={Selection} onEnter={authStates.requireLogin}/>
{/*Sex Selection*/}
<route path="/desire" component={Desire} onEnter={authStates.requireLogin}/>
<route path="/desire/saved" component={DesireSaved} onEnter={authStates.requireLogin}/>
<route path="/masturbation" component={Masturbation} onEnter={authStates.requireLogin}/>
<route path="/masturbation/saved" component={MasturbationSaved} onEnter={authStates.requireLogin}/>
<route path="/sex" component={Sex} onEnter={authStates.requireLogin}/>
<route path="/sex/saved" component={SexSaved} onEnter={authStates.requireLogin}/>
</Route>
</Router>
{/*<DevTools />*/}
</Provider>
)
}
}
I am unsure if you need more information, but basically inside my component console.info(this.props.store) is undefined
Maybe I am not thinking about it correctly?
connect doesn't magically populate props for child components of the connected one. It only passes store variables as props to the one you connect. If you want to pass them down further you must do so yourself:
let App = ({ foo }) => {
return React.cloneElement(this.props.children, { foo, somethingElse: 'blah' })
}
connect(state => ({ foo: state.foo }))(App)
If this makes sense for you, go for it, however it's probably more common to connect the component (or container, if you will) that is expecting the store data. There's nothing wrong with connecting hundreds of components.
class Sex extends Component {
// now you have access to store data and dispatch
}
export default connect(mapStateToProps, mapDispatchToProps)(Sex)

Categories