I am new to react-router and I just started writing an app using react-router V4. I would like to to pass props to components rendered by <Match /> and I am wondering about what's the 'best' or 'proper' way to do so.
Is it by doing something like this?
<Match pattern="/" render={
(defaultProps) => <MyComponent myProp = {myProp} {...defaultProps} />
}/>
Is this (passing props to components to be rendered by <Match />) even a good practice to do so with react-router or is it an antipattern or something; and if so, why?
You must use the render prop instead of component to pass on custom props, otherwise only default Route props are passed ({match, location, history}).
I pass my props to the Router and child components like so.
class App extends Component {
render() {
const {another} = this.props
return <Routes myVariable={2} myBool another={another}/>
}
}
const Routes = (props) =>
<Switch>
<Route path="/public" render={ (routeProps) =>
<Public routeProps={routeProps} {...props}/>
}/>
<Route path="/login" component={Login}/>
<PrivateRoute path="/" render={ (routeProps) =>
...
}/>
</Switch>
render() {
return (
<Router history={browserHistory}>
<Switch>
<Route path="/"
render={ () => <Header
title={"I am Title"}
status={"Here is my status"}
/> }
/>
<Route path="/audience" component={Audience}/>
<Route path="/speaker" component={Speaker}/>
</Switch>
</Router>
)
}
I'm fairly new to react-router and came across a similar issue. I've created a wrapper based on the Documentation and that seems to work.
// Wrap Component Routes
function RouteWrapper(props) {
const {component: Component, ...opts } = props
return (
<Route {...opts} render={props => (
<Component {...props} {...opts}/>
)}/>
)
}
<RouteWrapper path='/' exact loggedIn anotherValue='blah' component={MyComponent} />
So far so good
I use render in combination with a defined method like so:
class App extends React.Component {
childRoute (ChildComponent, match) {
return <ChildComponent {...this.props} {...match} />
}
render () {
<Match pattern='/' render={this.childRoute.bind(this, MyComponent)} />
}
}
The render prop is meant for writing inline matches, so your example is the ideal way to pass extra props.
I'll do it like the following to improve clarity.
const myComponent = <MyComponent myProp={myProp} {...defaultProps} />
<Match pattern="/" component={myComponent} />
By this your router code won't get messed up!.
Related
Hey guys i need to implement a button that will have a route assigned, i am writing on top of the existed code so I am a bit lost, there is a private route component wrapped in Switch component. How to i assign a route to my buttons onClick
<Switch>
<PrivateRoute exact path="/settings" component={Settings}/>
<PrivateRoute exact path="/admin" component={AdminButton}/>
The Private Route consist of the following
#inject('auth')
#observer
class PrivateRoute extends Component {
render() {
const {auth, component, ...rest} = this.props;
const RouteComponent = component;
const {authenticated, authenticating} = auth;
return (
<Route
{...rest}
render={props =>
(authenticated || authenticating) ? (
<RouteComponent {...props} />
) : (
<Redirect to="/login" />
)
}
/>
AdminButton component that needs to receive something to onClick i guess
export default class AdminButton extends React.Component {
render() {
return <QuestionButtonStyled onclick={}>Admin Panel</QuestionButtonStyled>
}
}
App.jsx
<DashboardLayout>
{this.props.auth.authenticated && <EpMenu/> }
<div style={{marginTop: '20px'}}>
<Switch>
<Route exact path="/login/:token?" component={LoginPage}/>
<PrivateRoute exact path="/welcome" component={Welcome}/>
<PrivateRoute exact path="/monthly-payslip" component={MonthlyPayslip}/>
<PrivateRoute exact path="/yearly-tax-overview" component={YearlyTaxStatementOverview}/>
<PrivateRoute exact path="/social-security-documents" component={SocialSecurityDocuments}/>
<PrivateRoute exact path="/central-information-documents" component={CentralDocuments}/>
<PrivateRoute exact path="/settings" component={Settings}/>
<PrivateRoute exact path="/admin" component={AdminButton}/>
<Redirect from="/*" to="/login"/>
</Switch>
</div>
</DashboardLayout>
Since you want to change the route on onClick event, you can make use of history from props and pass on the new path like
export default class AdminButton extends React.Component {
handleClick=() => {
this.props.history.push('/settings');
}
render() {
return <QuestionButtonStyled onClick={this.handleClick}>Admin Panel</QuestionButtonStyled>
}
}
I have been trying to understand nested routes and switch in the React v4 Router.
Consider the main router looks like this (simplified):
<Switch>
<Route path="/" component={LoginPage} exact={true} />
<Route path="/dashboard/edit/:id" component={DashboardPage} />
<Route path="/dashboard" component={DashboardPage} />
</Switch>
The "dashboard" component renders the sub-route:
render(){
return (
<div className="note">
<Route to='/edit/:id' render={(props) =>
<div>
<NoteList {...props} />
<EditNotePage {...props} />
</div>
} />
</div>
)
}
The "EditNotePage" component can access the param by:
const mapStateToProps = (state, props) => ({
note: state.notes.find((note) => note.id === props.match.params.id
});
Is this the correct approach?
It seems a little redundant to specify "/dashboard/edit/:id" twice ( ? )
Once in main router and the again in the dashboard component.
However, if I do not match the route in the main router "Switch" the "props.match.params.id" is not accessible since "props.match" will only point to "/dashboard" .
Have I missed something crucial regarding how the React v4 Router works? :)
Kind regards
Kermit
Nope, didn't miss anything. That's how react router v4 works. You define full routes. The trick you can use is that you can grab the current path and prepend it to your "nested path".
I have a simple React app and I'm trying to implement auth logic. This is how my application looks like:
class App extends Component {
render() {
return (
<Router history={history}>
<div className="App">
<Switch>
<PrivateRoute path="/account" component={Account} />
<Route component={PublicLayout} />
</Switch>
</div>
</Router>
)
}
}
My auth logic is: if user isAuthenticated === true render /account page else redirect to /signin. So for this I created my own simple PrivateRoute functional component:
export default ({ component: Component, ...args }) => {
return (
<div>
<Route {...args} render={(props) => {
return fakeAuth.isAuthenticated === true
? <Component {...props} />
: <Redirect to='/signin' />
}} />
</div>
)
}
Because PrivateRoute path="/account" is NOT exact path I expect that if user goes to /account-setting it will redirect him to /sign-in. But it's not working. The user successfully goes to /account-setting and see PublicLayout.
Why is that? There is no exact key in route, it has to grab all that starts from "/account" and use my logic in the PrivateRoute functional component.
Not providing exact props doesn't mean that it will match /account, /account-settings but it will match /account, /account/other, /account/other/paths. So, you'll need to provide both path:
<PrivateRoute path="/account" component={Account} />
<PrivateRoute path="/account-setting" component={Account} />
Or, you may use regex:
<PrivateRoute path="/account(?=\S*)([a-zA-Z-]+)" component={Account} />
I have a following situation:
<Wrapper>
<Container>
<Route exact path="/" component={UserListing} />
<Route path="/user/:id" component={UserDetails} />
<Route exact path="(/|/user/\d+)" component={StaticComponent} />
</Container>
<Route path="/create-account" component={CreateAccount}/>
</Wrapper>
Okey, so my case is simple: I don't want the Container component to render if path does not equal to "/" or "/user/:id".
So if path is "/create-account" only Wrapper with CreateAccount as child should appear, without Container.
Looking for some help. Thank you
You can write a custom component that decides whether or not to render Container like
const RouterRenderer = ({ location, children }) => {
if(location && location.state && location.state.noMatch) {
return null;
}
return children;
}
And a sub component which tells this container that nothing matched like
const NoMatch = () => {
return <Redirect to={{state: {noMatch: true}}} />
}
export default withRouter(NoMatch);
Now you can use them like
<Wrapper>
<RouterRenderer>
<Container>
<Switch>
<Route exact path="/" component={UserListing} />
<Route path="/user/:id" component={UserDetails} />
<Route exact path="(/|/user/\d+)" component={StaticComponent} />
<NoMatch />
</Switch>
</Container>
</RouterRenderer>
<Route path="/create-account" component={CreateAccount}/>
</Wrapper>
P.S. Note that its important to use Switch when you are using NoMatch component since you want to render it only when no other
route matched. Also you can write RouteRenderer function in the form
of an HOC instead of a functional component.
So I am running into the infamous React Router v4 warning:
Warning: You should not use <Route component> and <Route children> in the same route; <Route children> will be ignored
This happens after wrapping my component, which itself renders a <Route /> inside my HOC which provides my React context.
This all looks a bit like this:
App.js:
<myContextProvider>
<Router>
<Switch>
<myLayout path="/foo" Component={someComponent}>
...
</Switch>
</Router>
</myContextProvider>
my stores.js file includes the following:
export const withMainStore = (Component) => {
return (props) => {
return (
<MainConsumer>
{store => (
<Component {...store} {...props} />
)}
</MainConsumer>
)
}
}
myLayout.js:
class myLayout extends React.Component {
const { Component, ...rest } = this.props;
// ...
render() {
return (
<Route {...rest} render={(props) => (
<Component {...rest} {...props} />
)}/>
)
}
}
export default withMainStore(myLayout)
I hope my whole situation here is somewhat clear. can someone explain to me why the warning occurs? At NO point in my whole app, I have something like: <Route>...</Route> - I am always using <Route {...rest} render={(props) => ( ... )} />!
So I got no idea why this is happening. I has to have something todo with my withMainStore HOC, because when I remove it the error is gone. but still I do not understand why this causes the error.