How to pass query params in every single route path in react? - reactjs

I'm trying to implement React Router with query params like so http://localhost:3000/login?Id=1, I was able to achieve it only for my login route that too if I put path as http://localhost:3000/ which then redirects , however, I want to implement across the application. It matches nomatch route if I implement on other routes. This is how my index.js looks like, Can someone guide me how can i go about implementing all routes path including query params ?.
ReactDOM.render(
<BrowserRouter>
<Switch>
<Route
exact
path={`/`}
render={() => {
if (!store.getState().login.isAvailable) {
return <Redirect to={`/login?Id=${Id}`} />
} else {
return <Dashboard />
}
}}
/>
<Route exact path={`/login`} component={Login} />
<Route exact path={`/signup`} component={SignUp} />
{Routes.map((prop, key) => (
<Route path={prop.path} key={key} component={prop.component} />
))}
<Route component={NoMatch} />
</Switch>
</BrowserRouter>,
document.getElementById('root')
)

There are two ways about to accomplish what you want.
The most basic way would be on each "page" or root component of each route, handle the parsing of query params.
Any component that is the component of a Route component, will have the prop location passed to it. The query params are located in location.search and that will need to be parsed. If you are only worried about modern browsers, you can use URLSearchParams, or you can use a library like query-string, or of course, you can parse them yourself.
You can read more in the react-router docs.
The second way of doing this, really isn't that different, but you can have a HOC that wraps around each of your "pages" that handles the parsing of the query params, and passes them as a list or something to the "page" component in question.
Here's an example of the basic way using URLSearchParams:
import React from "react";
import { BrowserRouter as Router, Route, Link } from "react-router-dom";
// this is your "page" component, we are using the location prop
function ParamsPage({ location }) {
// you can use whatever you want to parse the params
let params = new URLSearchParams(location.search);
return (
<div>
<div>{params.get("name")}</div>
// this link goes to this same page, but passes a query param
Link that has params
</div>
);
}
// this would be equivalent to your index.js page
function ParamsExample() {
return (
<Router>
<Route component={ParamsPage} />
</Router>
);
}
export default ParamsExample;
EDIT: and to clarify, you don't need to do anything on your index.js page to make this work, the simple Routes you have should work fine.

Related

Creating wrapper component for react-router-dom v6 Route that logs route changes

We are looking to add custom logging to our react application, and would like to log each time a user changes routes. To handle this, we are creating a wrapper component , and this is what we currently have:
import React, { useEffect } from 'react';
import { Route } from 'react-router-dom';
function LoggerRoute(isExact, path, element) {
useEffect(() => {
// send route-change log event to our mongodb collection
}, []);
// And return Route
return (
isExact
? <Route exact path={path} element={element} />
: <Route exact path={path} element={element} />
);
}
export default LoggerRoute;
...and in our App.js file, we have changed the routes as such:
// remove this // <Route exact path='/tools/team-scatter' element={<TeamScatterApp />} />
<LoggerRoute isExact={true} path='/tools/team-scatter' element={<TeamScatterApp />} />
However, this throws the error Uncaught Error: [LoggerRoute] is not a <Route> component. All component children of <Routes> must be a <Route> or <React.Fragment>.
Additionally, it feels off passing props to a route, if possible we would prefer
<LoggerRoute exact path='/tools/team-scatter' element={<TeamScatterApp />} />
as the ideal way to call our LoggerRoute. We like the idea of a wrapper component, this way we don’t have to add logging into every component that our app routes to. However, I’m not sure if this wrapper component approach is possible if can only accept a component. How can we modify our LoggerRoute component to work / be better?
In react-router-dom#6 only Route and React.Fragment are valid children of the Routes component. Create either a wrapper component or a layout route component to handle listening for route path changes. Since you presumably want to do this for more than one route at-a-time I suggest the layout route method, but you can create a component that handles either.
Example:
import { Outlet, useLocation } from 'react-router-dom';
const RouteListenerLayout = ({ children }) => {
const { pathname } = useLocation();
useEffect(() => {
// send route-change log event to our mongodb collection
}, [pathname]);
return children ?? <Outlet />;
};
The children prop is used in the cases where you want to wrap individual routed components (i.e. the element prop) or an entire component, (*i.e. App) that is rendering a set of Routes. The Outlet component is used in the case where you want to conditionally include a subset of routes within a Routes component (i.e. some nested Route components).
Wrap the routes you want to listen to route changes for.
Examples:
<Routes>
<Route element={<RouteListenerLayout />}>
<Route path="path1" element={<SomeComponent />} />
<Route path="someOtherPath" element={<SomeOtherComponent />} />
... wrapped routes components with listener
</Route>
... routes w/o listener
</Routes>
or
<Routes>
<Route
path="/"
element={(
<RouteListenerLayout>
<SomeComponent />
</RouteListenerLayout>
)}
/>
</Routes>
or
<RouteListenerLayout>
<App />
</RouteListenerLayout>
These all assume the router is rendered higher in the ReactTree than RouteListenerLayout so the useLocation hook works as expected.
In v6, <Route> is a lot more strict than it was in v5. Instead of building wrappers for , it may be used only inside other <Routes> or <Route> elements. If you try to wrap a <Route> in another component it will never render.
What you should be doing instead is adding a wrapper component and leveraging it in the element prop on the route.
function App() {
return (
<Routes>
<Route path="/public" element={<PublicPage />} />
<Route
path="/route"
element={
<AddLogging>
<YourPage/>
</AddLogging>
}
/>
</Routes>
);
}
Edit: here is an example wrapper component based on your needs:
function AddLogging({children}) {
useEffect(() => {
// send route-change log event to our mongodb collection
// can use useLocation hook to get route to log
}, []);
return children;
}

Use URL parameter as a prop in React

I am trying to run an onLoad event to get data from my db. I need to use a url parameter as a the prop to do this with but can't figure out how to access it.
I am using react-router v6, although in my package.json it is called react-router-dom.
Here is my setup
index.js
const root = ReactDOM.createRoot(document.getElementById('root'))
root.render(
<BrowserRouter>
<Routes>
<Route path="/" element={<App />} />
<Route path="/newrecipe" element={<RecipeForm />} />
<Route path="/recipe/:name" element={<RecipeDBTest />} /> //relevant line
</Routes>
</BrowserRouter>
Then in RecipeDBTest.jsx I need to grab the final parameter from my url string that looks like this:
http://localhost:3000/recipe/toast
So I would grab toast from URL string and pass to server with a fetch request on the RecipeDBTest.jsx page.
Just hoping for some advice/guidance here.
You can use useParams hook of react-router.
import {
useParams
} from "react-router-dom";
function Child() {
// We can use the `useParams` hook here to access
// the dynamic pieces of the URL.
let { name } = useParams();
return (
<div>
<h3>Name: {name}</h3>
</div>
);
}
let name = this.props.match.params.name

Get router value from url in react

I have a react app, and a component that is supposed to load a set of value of the url
<PersistGate persistor={rootStore.persistor}>
<Router history={history}>
<ErrorBoundary>
<Preloader history={history} /> <== this need to read the url
<PasswordChecker></PasswordChecker>
<RouteDispatcher></RouteDispatcher> <== this contain the list of routes
</ErrorBoundary>
</Router>
</PersistGate>
In my RouteDispatcher path I have setup the route like this :
<BrowserRouter>
<Switch>
<PrivateRoute path="/ws/:ws/pj/:pj/ds/:ds" component={HomeDatastore} />
<PrivateRoute path="/ws/:ws/pj/:pj" component={HomeProject} />
<PrivateRoute path="/ws/:ws" component={HomeWorkspace} />
</Switch>
</BrowserRouter>
how would I get the value of ws=:ws pj=:pj etc...
I was thinking of doing it with a regex, but I wonder if there is a better way. Preloader is outside the routeDispatcher, so I don't seem to find a good way to get the value.
React router provides params into the component. Check props.match.params. I see that you have a custom component PrivateRoute so first check props in PrivateRoute.
May be try using useParams() hook. It gives you a way to access the URL params
Reference: https://blog.logrocket.com/react-router-hooks-will-make-your-component-cleaner/
Sandbox: https://codesandbox.io/s/react-router-useparams-hooks-q8g37
This might help you get started with it
import { useParams } from "react-router-dom";
function ComponentName() {
let params = useParams();
return (
<div>
{params.ws}
{params.pj}
</div>
);
}

How to create a route which points to a component in react

I have a table which is as shown below
I have a component which displays the details
How to display the details when click on the first tab of the table
i.e, when clicked on the number 1053 in the first column how to redirect to component with the parameter
Not sure how to build the route localhost:3000/#/sitepage/1053
Is there a way to directly get the route localhost:3000/#/sitepage/1053 so that i can give in the href of the table
Read about https://www.tutorialspoint.com/reactjs/reactjs_router.htm
but not sure how this link to route to write
Please point me to a document which will help me in doing
It you want to redirect to another component, you can use React Router Redirect
This section of React Router documentation will help you, since you haven't posted any code you tried I assumed you're looking for just some documentation to guide you.
Update:
Ok let's assume you have a Router like that:
<Router>
<Route exact path="/" render={(props) => <Table {...props} />}/>
<Route exact path="/column-details/:id" render={(props) => <ColumnDetails {...props} />}/>
</Router>
The props here has a history attribute, so in your <Table> you can have access to it by initializing your component like this
function Table({history}) {
// ...
}
Now in the first column, you need to add an onClick listener, let's say you use <TableCell> that would be
<TableCell onClick={() => history.push(`/column-details/${yourId}`)}/>
Now in your <ColumnDetails> component, we will need access to match attribute to extract our id from the url, so it would be something like:
function ColumnDetails({match}) {
const id = match.params.id;
// ...
}
I hope this is clear enough.
I think you can try react-router-dom to solve your problem, react-router-dom is a wrapper on top of react-router, which brings more flexibility
Let say your table is under Equipments component, then you have to add <Link/> component as alternate to <a> as follows, assuming your equipment object got url and title property
<Link to={`${equimpement.url}`}>{equimpement.title}</Link>
Please note that if you want to use Link component you have to import it on top
import { Link } from "react-router-dom";
And you can manage your routing in your App.js like this way
<div className="content">
<Switch>
<Route path="/not-found" component={NotFount} />
<Route path="/equipments/:id" exact component={EquipmentForm} />
<Route path="/equipments" component={Equipments} />
<Route path="/" exact component={Home} />
<Redirect to="/not-found" />
</Switch>
</div>
Make sure you have imported Route, Redirect and Switch as well in your on top of your App.js
import { Route, Redirect, Switch } from "react-router-dom";
Update
And last if you want to use Route in your app you must wrap your with BrwoserRouter
ReactDOM.render(
<BrowserRouter>
<App />
</BrowserRouter>,
document.getElementById("root")
);
with its import import { BrowserRouter } from "react-router-dom";

Prevent Navigation Component Rendering on Single Route but Render on every other Route

So I have a single Route /reminder, where I do not want my Navigation component, but I want the Navigation component to render on every other Route and I'm not sure how to go about this?
This is my App.js where I am using react-router-dom for my routing.
import React from "react";
import { BrowserRouter as Router, Route, Switch } from "react-router-dom";
import Navigation from "./components/navigation/Navigation";
import Reminder from "./components/reminder/Reminder ";
const App = () => {
return (
<>
<Switch>
<Route path="/reminder" component={Reminder} exact />
</Switch>
<Navigation />
<MainContent>
<Router>
<Route path={"/"} component={Dashboard} exact />
</Router>
</MainContent>
</>
);
}
I had a look around for similar issues, but it's mostly for authenticated pages, this isn't for an authenticated page.
I don't want to go down the route of having to add the Navigation component into each of my 21 current routes, it seems like there should a better method?
With my current code above, it renders the Reminder component but still renders the Navigation component.
You can access the match data using a custom hook from react-router. All you need to do is
import { useRouteMatch } from "react-router-dom";
function App() {
let match = useRouteMatch("/reminder");
// Do whatever you want with the match...
return (
<>
<Switch>
<Route path="/reminder" component={Reminder} exact />
</Switch>
{!match && <Navigation />} <---Conditional rendering
<MainContent>
<Router>
<Route path={"/"} component={Dashboard} exact />
</Router>
</MainContent>
</>
);
}
Also, while I didn't change this in your example, it's not generally a good idea to have a route outside of the router component. I'm actually a little surprised react-router-dom didn't throw an error for you.

Resources