React router is not reloading page when url matches the same route - reactjs

I have following route. When I'm at /createtest page and doing history.push(/createtest/some-test-id) as it matches the same route component, it is not reloaded. Is there any smart solution? Or I need to check match params and implement logic to reload?
(react 16, router v4)
<Route path="/createtest/:testid?/:type?/:step?/:tab?" render={(props) => <CreateTest user={user} {...props}/>}/>

You could give the URL parameters as key to the component so that an entirely new component will be created when a URL parameter changes.
<Route
path="/createtest/:testid?/:type?/:step?/:tab?"
render={props => {
const {
match: {
params: { testid, type, step, tab }
}
} = props;
return (
<CreateTest
key={`testid=${testid}&type=${type}&step=${step}&tab=${tab}`}
user={user}
{...props}
/>
);
}}
/>;

You can try this
const Reloadable = (props) => {
const { location } = props
const [key, setKey] = useState(location.key)
if (location.key !== key) {
setTimeout(() => setKey(location.key), 0)
return null
}
return <>{props.children}</>
}
<Route path="/" exact render={({ history, location }) => (
<Reloadable location={location}>
<SomeComponent />
</Reloadable>)}
/>

I think something like this might help solve your problem
<Route key={'notReloadableRoute'} ... />
(static key, like a regular string)

See this doc.
https://reacttraining.com/react-router/web/api/Route/exact-bool
The exact param comes into play when
you have multiple paths that have similar names:
For example, imagine we had a Users component that displayed a list of
users. We also have a CreateUser component that is used to create
users. The url for CreateUsers should be nested under Users. So our
setup could look something like this:
Now the problem here, when we go to
http://app.com/users the router will go through all of our defined
routes and return the FIRST match it finds. So in this case, it would
find the Users route first and then return it. All good.
But, if we went to http://app.com/users/create, it would again go
through all of our defined routes and return the FIRST match it finds.
React router does partial matching, so /users partially matches
/users/create, so it would incorrectly return the Users route again!
The exact param disables the partial matching for a route and makes
sure that it only returns the route if the path is an EXACT match to
the current url.
So in this case, we should add exact to our Users route so that it
will only match on /users:

Related

Routing components in React JS using login functionality

I am trying to build a full stack web application using Spring and React JS.
I have created login/register APIs in Spring and have connected them to React. They work just alright.
This is my UserService
import axios from 'axios';
const user_base_url = "http://localhost:8080/users";
class UserService{
createUser(user) {
return axios.post(user_base_url + '/register', user);
}
authenticateUser(user) {
return axios.post(user_base_url + '/login', user);
}
}
export default new UserService();
This how I validate my user in the LoginComponent.
validateUser = () => {
let user = {
username: this.state.email,
password: this.state.password,
};
UserService.authenticateUser(user).then((res) => {
if(res.data === 'SUCCESS') {
//logged in
console.log("logged in");
} else if(res.data === 'FAILURE') {
console.log("NO");
this.resetLoginForm();
this.setState({"error":"Invalid username or password"});
}
})
};
I now wish to add routes to my application so that certain components can only be accessed when logged in.
function App() {
return (
<div>
<Router>
<HeaderComponent/>
<div className="container">
<Switch>
<Route path="/" exact component={LandingPageComponent}></Route>
<Route path ="/customers" component = {ListCustomerComponent}></Route>
<Route path ="/add-customer/:id" component = {CreateCustomerComponent}></Route>
<Route path = "/view-customer/:id" component = {ViewCustomerComponent}></Route>
<Route path = "/admin-login" component = {AdminLoginComponent}></Route>
<Route path = "/admin-register" component = {AdminResgisterComponent}></Route>
</Switch>
</div>
<FooterComponent/>
</Router>
</div>
);
}
export default App;
How can this be achieved? I found solutions using tokens but I haven't used a token, I only check user is entering correct username and password from the database (MySQL) via my REST API.
Any help would be highly appreciated.
There are two types of Routes that are usually defined: Open Routes and Protected Routes.
Open Routes are the ones that are accessible by a user without any authentication and Protected Routes are the ones that require a certain form of authentication to be accessed.
Now, let's proceed to answer your questions.
How to Implement a Protected Route?
First, you need to know whether the user is authenticated or not and for the same, you will need to use a certain "value" or "token" (Like An ID Card) that says that this user is authenticated.
For a simple practise application, you could just store a Boolean saying whether the user is authenticated or not.
You will need to store this value in a place such as Local Storage, Cookies or Session Storage.
For this example, I have assumed that you are storing the value in a local Storage.
A Protected Route will be wrapped around with a condition that checks the Local Storage to find a value that says the user is authenticated.
isAuthenticated === true ===> Show The Desired Component.
isAuthenticated === false ===> Redirect the User to the login page.
// isAuthenticated is extracted from the local storage.
<Route path="/aClassfiedPath" render={() => (
isAuthenticated === true
? <DesiredComponent />
: <Redirect to='/login' />
)} />
You will also notice another practice, that is, to make a totally separate Layout for the Protected Components and inside the Layout, check whether the user is authenticated or not.
// Assuming the required Modules have been imported
const ProtectedLayout = ({children}) => {
if (!isAuthenticated) {
return (
<Redirect to="/loginPage" />
)
}
// children contains the Desired Component and `div` tag represents a custom
// container meant for Protected Routes. Like: A Speacial Header or Side Navigation.
return (
<div>
{children}
</div>
)
}
Recommended Read
An Article on Protected Routes
<Redirect> Reference
Render Prop of <Route>

Why is my custom hook not re-initialised on path change?

I am using ReactRouter to route the application (BrowserRouter at the top level) with a Switch that includes all the routes.
In my use-case I want to be able to handle paths that include the path-parameters (bedId) and navigate between different sub-paths (i.e. /beds/:bedId/, /beds/:bedId/info/) as well asa case where the path is (/beds/-).
I also want to be able to direct user to a different "bed" while they are already on some bed, so /beds/bed1/info -> /beds/bed2, and so on...
My BedView component is responsible for routing within that /beds/:bedId path like so:
// App.jsx (fragment)
<Switch>
<Route
path="/"
exact
render={() => (<Redirect to="/beds/-"/>)}
/>
<Route
path="/beds/-"
exact
component={SomeOtherComponent}
/>
<Route
path="/beds/:bedId"
component={BedView}
/>
</Switch>
The problem occurs when I try to use a hook that relies on the current path-parameter to fetch the latest data (i.e. call to /beds/bed1 will result in a call to http://myapi.com/beds/bed1/timeseries). The useLatestData hook is called from the BedView component, which look like so:
// BedView.jsx (fragment)
export default function BedView(props) {
const {bedId} = props.match.params;
let {path, url} = useRouteMatch('/beds/:bedId');
const bedData = useLatestData({
path: `/beds/${bedId}/timeseries`,
checksumPath: `/checksum/timeseries`,
refresh: false
});```
if(!bedData){
return <Loading/>;
}
return (
<Switch>
<Route exact path={path}>
<Redirect to={`${url}/info`}/>
</Route>
<Route exact path={`${path}/info`} >
<SomeComponent info={bedData.info} />
</Route>
</Switch>
}
...and the useLatestData hook is available here.
The problem is the fact that upon redirecting from /beds/bed1/info to /beds/bed2/info, the hook does not update its props, even though the BedView component seems to be re-rendering. I have created a version of the hook that 'patches' the problem by adding an useEffect hook in my custom hook, to detect the change in path (as supplied in the function arguments) and set data to null, but then the behaviour changes on the BedView.jsx's end - making the following check fail:
if(!bedData){
return <Loading/>;
}
I'm not entirely sure which part is the culprit here and any guidance would be much appreciated! As far as I'm aware, the hook is no re-initialised because the path change still results in the same component. There is also one caveat, once I change the BrowserRouter to include the forceRefresh flag, everything works fine. Naturally, I don't want my page to refresh with every redirect though...
try this:
const {bedId} = props.match.params;
let {path, url} = props.match;

React Router: Redirecting URLs with a specific param

I'm updating a data repository site where datasets are mapped to an id, which is the param used in our url paths. A few datasets got corrupted recently and part of the solution involved changing their ids. Problem is, a lot of users are linked to datasets on our site - some of which are dead now that those aforementioned ids have changed.
For now, I'm just doing a quick client-side redirect on the 5 or so ids that are dead. I just want to redirect the user from /datasets/oldID to /datasets/newID but I can't find anything in the docs about literally redirecting to a different url. Yep, hardcoding it.
If www.example.com/rootpath/dataset/001 is dead and is now www.example.com/rootpath/dataset/002, how can I redirect the user FROM www.example.com/rootpath/dataset/001 and TO www.example.com/rootpath/dataset/002?
Here's the dataset routes setup
const DatasetRoutes = ({ dataset }) => (
<Switch>
<Route
name="dataset"
exact
path="/datasets/:datasetId"
render={() => <DatasetContent dataset={dataset} />}
/>
<Route
name="download"
exact
path="/datasets/:datasetId/download"
component={DownloadDataset}
/>
<Route
name="publish"
exact
path="/datasets/:datasetId/publish"
component={() => (
<Publish datasetId={dataset.id} metadata={dataset.metadata} />
)}
/>
/* ...
more routes etc
*/ ...
</Switch>
)
I'm kind of baffled that I can't figure out how to do something so presumably simple with React Router v4. I've tried several things...is there a straightforward solution to this?
You can handle the redirect in DatasetContent component:
Set up a dictionary mapping old ids that require redirecting to new ids.
Use the useParams hook (or however you are accessing the params) and in the DatasetContent component:
const map = {
oldId: "newId"
};
let { datatsetId } = useParams();
if (map.hasOwnProperty(datasetId) {
return (<Redirect to={`/datasets/${map[datasetId]}`});
}
// the rest of your original DatasetContent rendering code.
...

Match 2 different routes with different parameters in React Router v4

I have a component that I'd like to match to 2 paths: / and /:value. My route looks like this:
<Route path="/(|:value)" render={(props) => { return <MyComponent/>}}
This above route does match /, but will NOT match /:value. If I hard code the route to:
<Route path="/(|mypath)" render={(props) => { return <MyComponent/>}}
it will match both / and /mypath. How can I get my route to match BOTH / and /any-value-i-put-here?
You just need to specify a Route with conditional parameter like
<Route path="/:value?" render={(props) => { return <MyComponent {...props}/>}}
and it will match / and /any-value. After this you can access the param if it exists like this.props.match.params.value
P.S. Also when you use render, make sure you pass the props to the
rendered 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