React router - accessing props within route - javascript

I'm new to React Router and i'm trying to pass down some props from one of my parent components down to a child one through a route.
Here is my code:
import React, {Component} from 'react';
import Sitefeedback from '../sections/comments/site-feedback';
import { BrowserRouter,Switch, Route } from 'react-router-dom';
class Outer extends Component {
constructor(props) {
super(props);
}
myFunction = () => {
console.log("hello");
}
render() {
<div className="app-content-wrap-inner">
<BrowserRouter>
<switch>
<Route path='/site-feedback' render={(props) => (<Sitefeedback {...props} />)}/>
<Route path='/logins' component={Logins} />
</switch>
</BrowserRouter>
</div>
}
}
Ideally I would like to be able to trigger myFunction from the child component. After doing some research I thought passing the Route a render option instead of component would pass it down however it doesn't. Any idea how I can get the function passed down into the routes component?

I don't know if you still need help with this, but if you do you can do it like this
<Route path='/site-feedback' render={props => (<Sitefeedback {...props} />)}/>

Related

React.js routing keeps rendering my main App regardless of URL

I am running an issue where, regardless of what URL I am putting into my browser, I keep getting routed to my main page. I've posted the code below for you to take a look, but my goal is to have my browser take me to my drivers.jsx component when the URL is localhost:3000/drivers. Currently, when I go to localhost:3000/drivers, it renders my _app.jsx component instead :(. Can someone help me understand why I can never render the Drivers component (in drivers.jsx) when I am at localhost:3000/drivers?
index.jsx:
import { BrowserRouter as Router, Routes, Switch, Route } from 'react-router-dom';
import MyApp from './_app.jsx';
import Drivers from './drivers.jsx'
import React, { Component } from 'react'
import { Link } from "../routes.js"
class Home extends Component {
render() {
return (
<Router>
<Switch>
<Route exact path='/drivers' element = {<Drivers />}> </Route>
<Route exact path='/' element = {<MyApp />}> </Route>
</Switch>
</Router>
);
}
}
export default Home;
_app.jsx
import React, { Component } from 'react';
import { useLoadScript } from '#react-google-maps/api';
import Map from '../components/map.jsx';
import "../styles/globals.css";
const MyApp = () => {
const libraries = ['places'];
const {isLoaded} = useLoadScript({
googleMapsApiKey: XXXXXXXXXXXXXX,
libraries
});
if (!isLoaded) return <div>Loading...</div>;
return (
<Map />
);
}
export default MyApp;
drivers.jsx
import React, { Component } from 'react';
class Drivers extends Component {
render() {
return (
<div>TEST</div>
);
}
}
export default Drivers;
I've tried putting the routing logic inside _app.jsx instead, but that causes an incredible amount of errors. My thought is index.js should host all the routing logic, but it shouldn't keep rendering MyApp instead of Drivers when the route is "localhost:3000/drivers".
if your react-router-dom version is
6.4.3
then the switch component dosen't work try changing code to this
instead of using Switch. wrap Route inside Routes
<Router>
<Routes>
<Route path='/drivers' element = {<Drivers />}> />
<Route exact path='/' element = {<MyApp />}> />
</Routes>
</Router>
like this

How do I pass props from from redux store

I have a simple app that's using redux and react-router. I wrapped my app component in a provider tag so that it has access to the store. I connected (in App.js) the mapStateToProps and mapStateToDispatch in the App.js. I'm not sure how to pass the function I defined in App.js to a child component since I'm using route. I tried doing the render trick but it didn't work. If I can pass it to that CelebrityPage component, how would I receive it in the file? Any help would be appreciated.
This is my App.js
import React, { Component } from 'react';
import { connect } from 'react-redux'
import './App.css';
import Clarifai from 'clarifai'
// import Particles from 'react-particles-js';
// import particlesOptions from './particleOptions'
import { Signin } from './components/signin/Signin';
import Register from './components/register/Register';
import { BrowserRouter as Router, Switch, Route } from 'react-router-dom'
import { setSearchField } from './context/Actions'
import FacePage from './Containers/FacePage';
import CelebrityPage from './Containers/CelebrityPage';
import ControllerPage from './Containers/ControllerPage';
const mapStateToProps = state => {
return {
input: state.input
}
}
const mapDispatchToProps = (dispatch) => {
return {
handleSearchChange: (event) => dispatch(setSearchField(event.target.value))
}
}
...
render() {
return (<Router>
<Switch >
<Route path='/celebrity' exact render={props => <CelebrityPage{...props} handleSearchChange={this.handleSearchChange} />} />
<Route path='/' exact component={Register} />
<Route path='/signin' exact component={Signin} />
<Route path='/contoller' exact component={ControllerPage} />
<Route path='/face-detection' exact component={FacePage} />
</Switch>
</Router>)
}
}
export default connect(mapStateToProps, mapDispatchToProps)(App)
If you are going to pass store actions and states into the child components, it means you are refusing to use the advantages of redux. The best approach should be connect any of your component that needs to access to the actions or state to the store. Doing connection at the root component level and passing the props to the child components is not a good solution.
I think what robert is saying is what you'd probably want to do. Don't try to pass your props inside of your <Route>. Instead do your connect mapDispatchToProps and your mapStateToProps inside your CelebrityPage Component.
Once you do the wrapping inside of the Celebrity Page component you should have access to the props and functions that you have defined.
...
// keep all the previous imports from your App.Js
render() {
// have your router like this
return (<Router>
<Switch >
<Route path='/celebrity' exact component ={CelebrityPage} />
<Route path='/' exact component={Register} />
<Route path='/signin' exact component={Signin} />
<Route path='/contoller' exact component={ControllerPage} />
<Route path='/face-detection' exact component={FacePage} />
</Switch>
</Router>)
}
}
export default App
Example Celebrity page
import React from 'react'
import { connect } from 'react-redux'
class CelebrityPage extends React.Component {
// put your mapStateToProps and mapDispatch function heres instead of app.js
mapStateToProps() {
}
mapDispatchToProps {
// bind your handlesearch function to props here
}
render() {
return (
<div>
<input />
<button onClick={this.props.handleSearchChange}/>
</div>
)
}
}
export default connect(mapStateToProps, mapDispatchToProps)(CelebrityPage)

withRouter Does Not passes Route props to component

Here is my navigation component:
import React from 'react'
class Navigation extends React.Component {
constructor(props) {
super(props);
this.state = {
type: 'signUp', // or login
showModal: false,
isLoggedIn: false,
}
}
...some code
render() {
const { showModal, type, isLoggedIn } = this.state
console.log(this.props.location); // all problem is this, I'm not getting it in console
return(
...some more code
)
}
}
export default withRouter(Navigation)
And here is where it it been used in app.js
class App extends React.Component {
render () {
return(
<Router>
<Fragment>
<Navigation /> // <= right there
<Switch>
<Route exact path='/' component={HomePage}/>
<Route exact path='/search' component={HomePage}/>
<Route component={Lost} />
</Switch>
</Fragment>
</Router>
)
}
}
I want to get updated route props like match and location and history in my <Navigation /> component but I get it only when the first time that component mounts on the DOM, in my other components I update the route using window.history.pushState but I am not able to get route props from withRouter after link in the browser is been updated.
I update route with window.history.pushState because:
I could not find any way to update just link in the address bar without showing user or redirecting user to new component with React router DOM (am I doing it in right way or not?)
based on that I then use window.location.pathname to add some specific stylings to some components)
Also, I read the entirety of this and this but I could not solve this issue. What am I doing wrong?
withRouter gives you the closest <Route>'s route props, and since the Navigation component is not inside a Route you will not get the route props.
You could e.g. put the Navigation component on a Route outside of the Switch that will always be visible.
Example
class App extends React.Component {
render() {
return (
<Router>
<Fragment>
<Route path="/" component={Navigation} />
<Switch>
<Route exact path="/" component={HomePage} />
<Route exact path="/search" component={HomePage} />
<Route component={Lost} />
</Switch>
</Fragment>
</Router>
);
}
}

Converting stateless React component having arguments to stateful

Inside my React JS project, I am working on the PrivateRoutes.
I have gone through this example of private routing and authenticating using react-router-dom.
https://reacttraining.com/react-router/web/example/auth-workflow
According to this documentation, they have created a PrivateRoute as a stateless component.
But my requirement is to convert it to stateful React component as I want to connect my PrivateRoute component to redux store.
Here is my code.
stateless component
import React from 'react';
import {Route, Redirect} from 'react-router-dom';
import {auth} from './Authentication';
const PrivateRoute = ({ component: Component, ...rest }) => (
<Route
{...rest}
render={props =>
auth.isAuthenticated ? (
<Component {...props} />
) : (
<Component {...props} action="login"/>
)
}
/>
);
export default PrivateRoute;
I converted this component to stateful React component like this.
stateful React component
import React from 'react';
import {Route, Redirect} from 'react-router-dom';
import {auth} from './Authentication';
import {connect} from 'react-redux';
class PrivateRoute extends React.Component {
render({ component: Component, ...rest }) {
return (
<Route
{...rest}
render={props =>
this.props.customer.isAuthenticated ? (
<Component {...props} />
) : (
<Component {...props} action="login"/>
)
}
/>
);
}
}
export default connect(state => state)(PrivateRoute);
Here, I am reading the data from redux store to check whether the user is authenticated or not.
But the way I am converting the stateless component to stateful isn't correct.
Am I passing the arguments render({ component: Component, ...rest }) correctly?
Will connecting the PrivateRoute with redux store create any problem with props as state=>state will map state to props as well as ...rest will have props object?
Not sure what is happening inside the code.
Update
AppRouter.js
import React from 'react';
import {BrowserRouter, Route, Switch} from 'react-router-dom';
import {TransitionGroup, CSSTransition} from 'react-transition-group';
import PrivateRoute from './PrivateRoute';
import HomePage from './../components/HomePage';
import AboutUs from './../components/AboutUs';
import ContactUs from './../components/ContactUs';
import PageNotFound from './../components/PageNotFound';
import RestaurantList from '../components/RestaurantList';
import RestaurantMenu from '../components/RestaurantMenu';
import UserDetails from '../components/UserDetails';
import OrderConfirmation from '../components/OrderConfirmation';
import CustomerAccount from '../components/CustomerAccount';
import Logout from '../components/sections/Logout';
export default () => {
return (
<BrowserRouter>
<Route render={({location}) => (
<TransitionGroup>
<CSSTransition key={location.key} timeout={300} classNames="fade">
<Switch location={location}>
<Route path="/" component={HomePage} exact={true}/>
<Route path="/about" component={AboutUs} />
<Route path="/contact" component={ContactUs} />
<Route path="/restaurants" component={RestaurantList} />
<Route path="/select-menu" component={RestaurantMenu} />
<PrivateRoute path="/user-details" component={UserDetails} />
<PrivateRoute path="/order-confirmation" component={OrderConfirmation} />
<PrivateRoute path="/my-account" component={CustomerAccount} />
<PrivateRoute path="/logout" component={Logout} />
<Route component={PageNotFound} />
</Switch>
</CSSTransition>
</TransitionGroup>
)} />
</BrowserRouter>
);
}
In general, converting a stateless functional component (SFC) to a Component is done like this:
Create the class shell for it.
Copy the SFC's body to the render method. If the SFC was an arrow function, add a return as necessary to render.
Change any references to props in the render method to this.props (or just add const { props } = this; at the top). SFCs receive their props in their arguments, but a component receives them as arguments to its constructor; the default constructor will save them as this.props.
In your case, it's using destructuring on its arguments, so you could do the same with this.props on the right-hand side of the destructuring:
const { component: Component, ...rest } = this.props;
That's it. In your code, you've added parameters to the render function, but it doesn't get called with any arguments, and you've only changed props to this.props a bit haphazardly (including changing auth.isAuthenticated to this.props.customer.isAuthenticated for some reason).
So applying 1-3 above:
// #1 - the shell
class PrivateRoute extends React.Component {
// #2 - `render`, with the body of the SFC inside
render() {
// #3 - destructure `this.props`
const { component: Component, ...rest } = this.props;
// #2 (part 2) - add `return`
return <Route
{...rest}
render={props =>
auth.isAuthenticated ? (
<Component {...props} />
) : (
<Component {...props} action="login"/>
)
}
/>;
}
}
Your stateful component should be:
class PrivateRoute extends React.Component {
render() {
const { component: Component, ...rest } = this.props;
return (
<Route
{...rest}
render={props =>
this.props.customer.isAuthenticated ? (
<Component {...props} />
) : (
<Component {...props} action="login"/>
)
}
/>
);
}
}
Please see that there is some issue in render parameter of Route. Here you have props as function param but still using this.props.customer, don't know the use case hence please fix it as per your application.
Apart from it Component and all the other data is already there in props of the component. It won't be available in parameter of render method in component. Same destructuring as available in stateless component can be written in render method as shown in code above.
Will connecting the PrivateRoute with redux store create any problem with props?
Yes, it would. The way you have connected to the store will make store data available in props of component but external props passed to component will not be available.
For that you have to handle it in mapStateToProps function:
const mapStateToProps = (state, ownProps) => ({
...state,
...ownProps
});
Here mapStateToProps has second parameter which has the external own props passed to component. So you have to return it as well to make it available in component props.
Now connect would be like:
export default connect(mapStateToProps)(PrivateRoute);
I was having two queries.
1) How to convert to Stateful Functional Component?
2) After connecting to the redux store will the props create a problem?
My first query was solved by the answer provided by T.J.Crowder.
For a second query, I tried connecting the redux store to the PrivateRoute and I did get the data I was looking for.
Here is the code which worked for me.
import React from 'react';
import {Route, Redirect} from 'react-router-dom';
import {connect} from 'react-redux';
class PrivateRoute extends React.Component {
render() {
const { component: Component, ...rest } = this.props;
const {customer} = this.props;
return <Route
{...rest}
render={props =>
customer.isAuthenticated ? (
<Component {...props} />
) : (
<Component {...props} action="login"/>
)
}
/>;
}
}
export default connect(state => state)(PrivateRoute);
Using this code I got the data that is coming from the routes, as well as the redux state inside the props.
This is getting data coming from the routes
const { component: Component, ...rest } = this.props;
This is the data coming from the redux store.
const {customer} = this.props;
#T.J.Crowder has already written how to convert stateless component to stateful component in those 3 steps. so i will just write about connecting component to redux store like you did.
I think connected components should always define mapStateToProps and explicitly declare which data they depend on from the state.
because the connected component rerenders if the connected property changes. so it would be a bad idea to connect the whole application state to a component. as it would mean that wheneever anything changes in application state rerender all connected components.
better we define explicitly like the following that we depend on a property called data (or anything you have) from the state. so in this case this component will only rerender if state.data changes it wont rerender if state.xyz changes.
and this way you can take state.data and name it as you wish so it would not conflict with any existing props of the component.
const mapStateToProps = (state, ownProps) => ({
data: state.data
});
export default connect(mapStateToProps)(PrivateRoute);

React Router: access history in rendered Route component

I know there are quite a few questions on this, but I cannot seem to get it to work: I need to access "history" from the child components that are rendered through the Routes. (It receives props from a redux container).
I need to pass down the history object to the components that are rendered in each Route, so that I can this.props.history.push('/route') in the child components. This application was less dynamic before, so each Route was hardcoded with a component={someComponent}; but I found that in doing the Routes dynamically, you need to use render={() => <someComponent />}.
After changing the Routes from component={} to render={() => ...} I lost history in the child components.
My code is something like:
import React, {Component} from 'react';
import ReactDOM from 'react-dom';
import { HashRouter as Router, Link, Route, Redirect } from 'react-router-dom';
import { Nav_Item } from '.'
import DynamicTab from '.';
export default class NavBar extends React.Component {
constructor(props) {
super(props);
this.state={}
}
render() {
let tabs = [];
let routes = [];
this.props.tabs.forEach( function(tab, index) {
tabs.push(<Nav_Item key={tab.name} path_name={'/' + tab.name} tab_text={tab.label} />);
routes.push(<Route key={tab.name} path={'/' + tab.name} render={() => <DynamicTab tabName={tab.name} tabSpecs={tab} />} />);
})
return (
<Router>
<div>
<div>
<ol>
{ tabs }
</ol>
</div>
<Redirect to={'/' + this.props.tabs[0].name} />
{ routes }
</div>
</Router>
)
}
}
When you use render you actually get props. In the docs they have an example like this:
<Route {...rest} render={props => (
<FadeIn>
<Component {...props}/>
</FadeIn>
)}/>
So you should be able to access history from those props.
Another solution would be to conditionally render a <Redirect/> component. So maybe you have some internal state that you use like this:
// in your components render function..
if(this.state.shouldRedirect) {
return (<Redirect to={yourURL} />);
}
this.props.router and you have access to whatever you want.
in your case you can just do:
this.props.router.push("/newurl");
There is no need to pass the history as a separate property if this component is rendered by the router.

Categories

Resources