Conditionally enable Route in React - reactjs

I have created an <Authorise /> component which renders either a 'yes' or 'no' prop based on a boolean 'allowed' prop.
export const Authorise = ({
allow = false,
yes = () => null,
no = () => null
}) => (allow ? yes() : no());
<Authorise /> component works as expected however when I try to render a <Route />, only the first route is rendered and the rest of the <Route /> components are ignored.
Why is this happening? If I render the routes outside of the <Authorise /> component it works fine. Only when rendering the components via the <Authorise /> component does it not work and I can't work out why.
Check out my example to see the issue:
CodeSandbox

It's because of
https://reactrouter.com/web/api/Switch/children-node
All children of a <Switch> should be <Route> or <Redirect> elements. Only the first child to match the current location will be rendered.
If you're using a library but not playing by its rule, then something like this is what was expected to happen.

The issue is not with your Authorize function, but because of the Route Component which will only display when the path is matched. The second issue it with Switch which will make it that only the first matched route to be displayed
With Switch added the "/" path will always be hit since it's the first one. If you try to remove your Switch (or move the route with "/" at the end) and go to "/next" on your local dev then the /next route will be displayed

Related

React router two URL parameters &more

I'm using react-router-dom and this is currently how it looks:
<Switch>
<Route path={"/layouts/:layoutID"} component={Layouts} />
<Route
path={"/dashboard/:dashboardID"}
component={Dashboards}
/>
</Switch>
When the user navigates to "/dashboard/:dashboardID" inside this component he can choose a sub-page onClick, and I want the URL structure will be "/dashboard/:dashboardID/:pageID" pageID will navigate to 'PageIdComponent' this component will get a 'match' props and will show pageID
Please see the attached file that shows the necessary structure.
What is the best way to implement it?
If you have the child route inside your Dashboard component, then you can use the match.path value to build up a sub route:
<Route path={`${props.match.path}/:dashboardId`}>} component={SubDashboard} />
in order to make the path match what the parent's had and then add a new variable on top of that.
Then you can navigate to it using the match.url to make a link to the current URL and add the subPage to it:
<Link to={`${props.match.url}/pageFour`}>Page four</Link>
Instead of prop driling, React Router offers ways to get the relevant props to whatever components you desire via withRouter HOC or various hooks. The hooks were introduced in version 5.1
To get the match via a hook you can use const match = useRouteMatch()
To get the relevant props via the HOC:
const Example = useRouteMatch(function Example({ match, location, history }) {
return <div>{match.url}</div>;
});
For react router v6 (that's upcoming):
https://reacttraining.com/blog/react-router-v6-pre/#nested-routes-and-layouts

Redirect to another component after form submission in reactjs

I have the following method to handle form submission-
handleSubmit(event) {
event.preventDefault();
axios.get('url').then(response => {
if(response.data.issuccess){
this.props.history.push("/SuccessComponent");
}
});
}
It changes the URL in addressbar, but UI remains the same.
How to solve this?
This method will not work perfectly.
If you're using react-router-dom package, you need to initially set the route of your successCompoment in the root file mostly app.js.
import SuccessComponent from .....link to the file...
<Route path='/SuccessComponent' component={SuccessComponent} exact/>
Applying this will work.
If you didn't define the route to a component, you cannot Make a navigate to a URL expecting to render different component not initially defined

Can useParams be able to use inside App.js?

Good day, hoping that I am not bothering you guys.
Details
I have a path like this
localhost:3000/Project-1/todo-1
and Switch of Route like this
<Route path='/:project/:todo' component={Todo} />
Expected Output
When browser is in the example path, what I expected is App.js can also get params object by using useParams but shows empty. Did I misuse the hook? Thank you for the answers.
Additional Details
I did use useLocation that returns something like path but that is not my aim, it should be something like
params: {
project: 'Project-1',
todo: 'todo-1 '
}
that is returned using useParams for ease of extraction of project and todo values
I believe Route injects props to components it renders, so you should be able to pull it straight from the match prop within Todo: this.props.match.params.
You’ll have access to match objects in various places:
Route component as this.props.match
Route render as ({ match }) => ()
Route children as ({ match }) => ()
withRouter as this.props.match
matchPath as the return value
https://reacttraining.com/react-router/web/api/match
This only works for components rendered within a router on a route, i.e. the component a Route is rendering, or a child component further down the DOM tree. If App.js is rendering the router it won't have it.
If you need to do something with route params in the root App.js you can wrap it in a route, with unspecified path so it matches all routes, in your router using the render prop. Just ensure this is rendered outside any Switch components though as switches only return the first match, not all matches.
<Route
render={({ match }) => {
console.log('match', match);
// do what you need with the `match` prop, i.e. `match.params`
return null; // return null to indicate nothing should actually be rendered
}}
/>

ReactJS - link to doesn't work for URL with parameter

I use ReactJS and react-router-dom (version 4.2). I can't have my links working with :
<Link to={'/site/' + site.id}>
<h5><Icon name="map-marker"/> {site.title}</h5>
</Link>
It works when I am on home page (http://localhost:3000), the component is rendered, but when I am on a page like : http://localhost:3000/site/1, I can't go to URL http://localhost:3000/site/2.
The URL changes in address bar, but nothing happens.
I use :
...
<Route path="/site/:id" render={(props) => (
<SitesView id={props.match.params.id} />
)}/>
...
export default connect(mapStateToProps, mapDispatchToProps, undefined, { pure: false })(App);
In my Router. I can't get it, is it because the URL are almost the same ?
As I understood , you have one component <SitesView/> for all of the /site/ routes with different props.id's.
When you change the parameter of URL, the id of <SitesView/> component and the URL will be changed but be noticed the <SitesView/> is rendered on previous route and at the moment you are seeing this rendered component which has no knowledge aboute the changes.
Then you need to use one of the React LifeCycle Methods named componentWillReceiveProps(nextProp, nextState) to get to know when the id of <SitesView/> changed. and based on new id you can update your component.

React. How to redirect early in a component lifecycle

This seems so simple, but I am new to react and trying different approaches and nothing is working for me. (BTW I am a regular contributor but working on my clients machine and can't remember my so password.)
The version of react router is 4.0, and state is stored using redux.
The scenario is that we are changing the order of routing in our application and would like to redirect any users that have Urls with the old structure to the new Url structure. I have tried the following (also note that I have "scrubbed" the names of the page, function calls and variables):
There is a trigger component for the section I need to direct from, with routing set up like this:
<Route path='page/:pageGuidGuid' component={PageTrigger}> </Route>
In the trigger component, ComponentWillMount makes a request that returns a 404 if the link is from the previous component, although it does redirect to the correct route. I am checking for and triggering the redirect in getInitialState, but the component keeps going through the lifecycle and ComponentWillMount is called, resulting in a 404 and then redirects to the correct page.
const PageTrigger = connectToStores(React.createClass({
getInitialState() {
this.checkForRedirect();
return {};
},
componentWillMount() {
if (this.props.params.guid) {
this.setCaseByGuid(this.props.params.guid);
}
},
checkFrorRedirect() {
/* logic to determine if it should redirect */
browserHistory.push(redirectUrl);
}
}
I have also tried creating a custom route...
<CaseRedirectRoute path='(cases/:caseGuid)' component={CaseTrigger}>
</CaseRedirectRoute>
And in a separate module (based off a login sample)
const CaseRedirectRoute = ({ component: Component, ...rest }) => (
<Route
{...rest} render={props => (
checkForCaseRedirect(...props) ? (
<Redirect to={{
pathname: getCaseUrlRedirectUrl(...props),
state: { from: props.location }
}} />
) : (
<Component {...props} />
)
)} />
);
I have WithRouter imported from react router. When I try to run this I am getting an error inside the react framework:
Uncaught Type Error: Cannot read property 'createRouteFromReactElement' of undefined
at RouteUtils.js:68
at forEachSingleChild (ReactChildren.js:52)
at traverseAllChildrenImpl (traverseAllChildren.js:98)
at traverseAllChildrenImpl (traverseAllChildren.js:114)
at traverseAllChildren (traverseAllChildren.js:186)
at Object.forEachChildren [as forEach] (ReactChildren.js:70)
at createRoutesFromReactChildren (RouteUtils.js:65)
at Function.createRouteFromReactElement (RouteUtils.js:35)
at RouteUtils.js:69
at forEachSingleChild (ReactChildren.js:52)
I've tried redirecting from app.js and page.js, but the PageTrigger is the first component having state set. I either need to figure out how to stop execution after the redirect, or figure out why my custom route keeps blowing up. Any help would be appreciated.
I'm not sure about your setup, but I would implement this redirect as this:
<Route path='oldurl/:p1/:p2' render={
routeProps => (
<Redirect to={'/newUrlHere/'+routeProps.match.params.p1} />
)
} />
So, if the route matches (you may specify exact match if necessary), when the Redirect "renders" it stops rendering and should start the rendering from the new URL. You may build your URL in the Redirect.to property as you wish

Resources