How to set React.useState based on URL - reactjs

I am trying to set the following to different numbers based on the page the user is on using window.location.path
const [value, setValue] = React.useState(1);
I have tried if statement and I seem to get errors so my thinking was to set a params to
<Route exact path="/" component={Home} />
however while I understand I could do something like
<Route exact path="/:id" component={Home} />
the issue is the following as you can see below I have a number of set paths, the tabs in the nav bar are linked to the main path (/, /about, /news, /programs etc)
<Switch>
<Route exact path="/" component={Home} />
<Route path="/about/" component={About} />
<Route path="/news/:id" component={News} />
<Route path="/programs/:id" component={Programs} />
<Route path="/podcast/:id" component={Podcast} />
</Switch>
I want to be able to send a number with each Route so that way I can just set the following with something like
const [value, setValue] = React.useState({pageID});
I was thinking something like
<Route path="/podcast/:id" pageID="4" component={Podcast} />
How can I do this?

let say that you are using this in component News
The first step to be able to extract the param out of the link is using withRouter
Import it in your component News file: import { withRouter } from "react-router";
We need to wrap the component inside the withRouter HOC as follows:
//instead of exporting it as export default News.
export default withRouter(News) //this will make the API we need available at the component's props
supposing you want to do that:
const [value, setValue] = React.useState({pageID});
at the component mount, inside componentDidMount we can extract the param as follows:
const { id } = this.props.match.params //the id the user navigated to: /news/:id
and then you can use the id to update your state.

You can try to pass parameters to component with render method.
<Switch>
<Route exact path="/" render={(props) => <Home pageId=1 {...props} />} />
<Route path="/about/" render={(props) => <About pageId=2 {...props} />} />
</Switch>

You can add multiple params to the path as -
path="/podcast/:id/:pageId"
And to retrieve -
this.props.match.params.pageId

Related

<Route component> and <Route children> in the same route

Warning: You should not use Route component and Route children in the same route; Route component will be ignored
import { BrowserRouter, Route, Switch } from "react-router-dom"
import { useState } from "react";
import SideBar from "./SideBar";
import Playing from "./Playing";
import "./App.css";
import AllSong from "./Components/AllSong";
import Favourites from "./Components/Favourites";
function App() {
const [sidebar, setSidebar] = useState(false);
return (
<BrowserRouter>
<div className="box image photo">
<SideBar sidebar={sidebar}/>
<Switch>
<Route path="/" exact component={Playing}>
<Playing sidebar={sidebar} setSidebar={(bool) => setSidebar(bool)}/>
</Route>
<Route path="/AllSong" component={AllSong}/>
<Route path="/Favourites" component={Favourites}/>
</Switch>
</div>
</BrowserRouter>
);
}
export default App;
Can someone explain why is the warning coming and also help to improve the code. (I'm new to react)
I tried using the v6 for react-router and react-router-dom b ut it showed number of errors so i degraded again and used switch again
Thanks You.
This is happening because you have <Route path="/" ...> & within that you have <Playing... so this confuses the Router & it doesn't know whether to render the parent <Route ... or the child <Playing... hence the error.
Like the warning says, you can't specify both a route component and wrap children components.
From the docs:
Warning: <Route children> takes precedence over both <Route component> and <Route render> so don’t use more than one in the
same <Route>.
The issue is specifically with:
<Route path="/" exact component={Playing}>
<Playing sidebar={sidebar} setSidebar={(bool) => setSidebar(bool)}/>
</Route>
You are rendering Playing in both places. Since it seems like you want to pass additional props into Playing then I suggest using the render prop so Playing can also still receive route props, i.e. history, location, and match props.
<Route
path="/"
exact
render={props => (
<Playing
{...props}
sidebar={sidebar}
setSidebar={(bool) => setSidebar(bool)}
/>
)}
/>
See Route render methods for further clarification on rendering routed content by a Route component.
it is specifically these lines:
<Route path="/" exact component={Playing}>
<Playing sidebar={sidebar} setSidebar={(bool) => setSidebar(bool)}/>
</Route>
you're setting both the component
component={Playing}
and the children
<Playing sidebar={sidebar} setSidebar={(bool) => setSidebar(bool)}/>
React will only render one of them so one of them has to be ignored, in this case the component.
If you remove one of them the warning will disappear.

How can I put a route like "/items?Search="?

I have been assigned a task where I must put a route with the following url: /items?search= to load SearchBarScreen component.
I have this code and I am using react-router-dom package:
export const AppRouter = () => {
return (
<Router>
<Switch>
<Route exact path="/items?search=" component={SearchBarScreen} />
<Route exact path="/product/:id" component={ProductDetailScreen} />
<Route path="/" component={HomeScreen} />
<Route component={NotFoundScreen} />
</Switch>
</Router>
);
I can't load my SearchBarScreen component with the url:
http://localhost:3000/items?search=XDDD
the route HomeScreen (/) is loaded in this case :(
what am I doing wrong?
You should feed the URI without the query string to the Route component. So if you want to render your SearchBarScreen at this URL http://localhost:3000/items?search=, you should do this:
<Route exact path="/items" component={SearchBarScreen} />
And then access the query string inside SearchBarScreen like so:
const SearchBarScreen = ({ match, location }) => {
console.log(location.search);
return <div>Search results</div>;
};
Another way is to drop the exact prop.
My recommended solution is the former. Here is a Sandbox: https://codesandbox.io/s/elated-http-lkqfj?file=/src/App.js
The exact param is used when you have multiple paths that have similar names:
for exmplae in your code when you go to //localhost:3000/items?search=XDDD the router will go through all of our defined routes and return the FIRST match it finds.And this is the first thing it finds //localhost:3000/ And executes the HomeScreen component.
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,you should add exact to / route:
<Router>
<Switch>
<Route path="/items?search=" component={SearchBarScreen} />
<Route path="/product/:id" component={ProductDetailScreen} />
<Route exact path="/" component={HomeScreen} />
<Route component={NotFoundScreen} />
</Switch>
</Router>
You can do something like this
You can use this custom hook
useLocation hook is imported from react-router-dom
useQuery() {
return new URLSearchParams(useLocation().search);
}
Then in component
const query = useQuery();
Docs Query param example

Passing props from higher level component to lower level component, React Router in play

I currently have all the Routes in my app defined in App.js. Would like to be able to pass state (as props) from the Alignment component down to the GPfSOA component.
function App() {
return (
<Router>
<div className="App">
<Nav />
<Switch>
<Route path="/" exact component={Home} />
<Route path="/about" exact component={About} />
<Route path="/alignments" exact component={Alignments} />
<Route path="/alignments/:id" exact component={Alignment} />
<Route path="/alignments/segmentinfo/:id" exact component={Segments} />
<Route path="/alignments/segmentinfo/:id/:segid" exact component={Segment} />
<Route path="/alignments/getpoint/:id" exact component={GPfSOA} />
<Route path="/alignments/getstatoff/:id" exact component={GSOfPA} />
<Route path="/alignments/getalsfromxy/:x/:y" exact component={AlignList} />
<Route path="/alignments/getsegsfromxy/:x/:y" exact component={SegmentList} />
<Route path="/alignments/post/create" exact component={AddAlignment} />
<Route path="/alignments/put/update/:id" exact component={EditAlignment} />
<Route path="/alignments/ptso/list" exact component={TogglePoints} />
<Route path="/alignments/ptso/list/:ptid" exact component={Point} />
<Route path="/" render={() => <div>404</div>} />
</Switch>
</div>
</Router>
);
}
The order from parent on down to the greatest grandchild would be App > Alignments > Alignment > GPfSOA. Trying to pass item.alignment (the alignment's name) from the Alignment component down (or over) to the GPfSOA component so that it can be rendered there. item.alignment is a property of the Alignmnet component's state.
Do I need to set these up as nested routes in order to accomplish this (a.k.a. cut and paste all the Routes from App.js that are children of the Alignment component and paste them into the Alignment component)?
Having a hard time understanding how to define a particular component as being a parent and another component as being a child of that component. All the examples I see assume you want to pass props from App.js down to some other component. Looking for examples with React Hooks and React Router in play (functions rather than classes) where you're passing props from a component 'below' App.js down to another component that's further down in the hierarchy. Hope this makes sense.
Found lots of examples such as this one for 'passing function as a render props in Route component' (supposedly the recommended way to do this)
const PropsPage = () => {
return (
<h3>Props Page</h3>
);
};
const App = () => {
return (
<section className="App">
<Router>
...
<Link to="/404-not-found">404</Link>
<Link to="/props-through-render">Props through render</Link>
<Switch>
...
<Route exact path="/props-through-render" render={(props) => <PropsPage {...props} title={`Props through render`} />} />
<Route component={NoMatchPage} />
</Switch>
</Router>
about with browser reload
</section>
);
};
export default App;
But like I stated before, this example and every other one I've found assume you want to pass props from App.js down to another component.
Your issue can be handle with creating Alignment context
import React, { createContext, useState } from "react";
const AlignmentContext = createContext();
const AlignmentContextProvider = ({ children }) => {
const [num, setNum] = useState(1);
};
return (
<AlignmentContext.Provider value={{ num, setNum }}>
{children}
</AlignmentContext.Provider>
);
};
export { AlignmentContext, AlignmentContextProvider };
now wrap your routes needed to be in same context with AlignmentContextProvider
import { AlignmentContextProvider } from 'pathto/context'
<AlignmentContextProvider>
<Route path="/alignments/:id" exact component={Alignment} />
<Route path="/alignments/segmentinfo/:id" exact component={Segments} />
<Route path="/alignments/segmentinfo/:id/:segid" exact component={Segment} />
<Route path="/alignments/getpoint/:id" exact component={GPfSOA} />
</AlignmentContextProvider>
and use useContext hooks for reach values
import React, { useContext } from "react";
import { AlignmentContext } from 'pathto/context';
const GPfSOA = () => {
const { num, setNum } = useContext(AlignmentContext);

Route parameters in react

I have many routes
<Route exact path="/" component={Test} />
<Route exact path="/1" component={Test1} />
<Route exact path="/2" component={Test2} />
In every component i use useLocation to get the data from route. It exists a possibility to pass in Route a parameter and to access that parameter with useLocation, in every component?
Here is the example:
<Route exact path="/" parameter='this is route nr 1' component={Test} />
<Route exact path="2/" parameter='this is route nr 2' component={Test2} />
Which is the solution to do what i want to achieve?
For query params you don't need to do anything extra in <Route />. You just need to get those query params in component and parse it
To access param1 from url
/?param1=hello
import { useLocation } from 'react-router-dom';
const Test = () => {
const queryParams = new URLSearchParams(useLocation().search);
return (
<div>Test {queryParams.get('param1')}</div>
);
}
And if you want path params like
/1 // 1 from this route
In routes and you don't need to create multiple routes just to get 1,2 etc
<Route exact path="/:id" component={Test} />
In component
import { useParams } from 'react-router-dom';
const Test = () => {
let { id } = useParams();
return (
<div>Test ID: {id}</div>
);
}
It seems from your question that you search for a way to pass data to routes only at the router declaration. therefore you can use regular props instead of location data extract -
<Route exact path="/" render={()=><Test parameter={'this is route nr 1'} />} />
You can either pass props to the component directly using render:
<Route
exact
path="/"
render={props => <MyComponent {...props} foo="hello world" bar={false} /> }
/>
or you can use query params:
<Route
exact
path={`/user?id=${user_id}`}
component={MyComponent}
/>
and then in MyComponent you can access props.match.params.id
React-router docs are a great start

React-Router - Route re-rendering component on route change

Please read this properly before marking as duplicate, I assure you I've read and tried everything everyone suggests about this issue on stackoverflow and github.
I have a route within my app rendered as below;
<div>
<Header compact={this.state.compact} impersonateUser={this.impersonateUser} users={users} organisations={this.props.organisations} user={user} logOut={this.logout} />
<div className="container">
{user && <Route path="/" component={() => <Routes userRole={user.Role} />} />}
</div>
{this.props.alerts.map((alert) =>
<AlertContainer key={alert.Id} error={alert.Error} messageTitle={alert.Error ? alert.Message : "Alert"} messageBody={alert.Error ? undefined : alert.Message} />)
}
</div>
The route rendering Routes renders a component that switches on the user role and lazy loads the correct routes component based on that role, that routes component renders a switch for the main pages. Simplified this looks like the below.
import * as React from 'react';
import LoadingPage from '../../components/sharedPages/loadingPage/LoadingPage';
import * as Loadable from 'react-loadable';
export interface RoutesProps {
userRole: string;
}
const Routes = ({ userRole }) => {
var RoleRoutesComponent: any = null;
switch (userRole) {
case "Admin":
RoleRoutesComponent = Loadable({
loader: () => import('./systemAdminRoutes/SystemAdminRoutes'),
loading: () => <LoadingPage />
});
break;
default:
break;
}
return (
<div>
<RoleRoutesComponent/>
</div>
);
}
export default Routes;
And then the routes component
const SystemAdminRoutes = () => {
var key = "/";
return (
<Switch>
<Route key={key} exact path="/" component={HomePage} />
<Route key={key} exact path="/home" component={HomePage} />
<Route key={key} path="/second" component={SecondPage} />
<Route key={key} path="/third" component={ThirdPage} />
...
<Route key={key} component={NotFoundPage} />
</Switch>
);
}
export default SystemAdminRoutes;
So the issue is whenever the user navigates from "/" to "/second" etc... app re-renders Routes, meaning the role switch logic is rerun, the user-specific routes are reloaded and re-rendered and state on pages is lost.
Things I've tried;
I've tried this with both react-loadable and React.lazy() and it has the same issue.
I've tried making the routes components classes
Giving all Routes down the tree the same key
Rendering all components down to the switch with path "/" but still the same problem.
Changing Route's component prop to render.
Changing the main app render method to component={Routes} and getting props via redux
There must be something wrong with the way I'm rendering the main routes component in the app component but I'm stumped, can anyone shed some light? Also note this has nothing to do with react-router's switch.
EDIT: I've modified one of my old test project to demonstrate this bug, you can clone the repo from https://github.com/Trackerchum/route-bug-demo - once the repo's cloned just run an npm install in root dir and npm start. I've got it logging to console when the Routes and SystemAdminRoutes are re-rendered/remounted
EDIT: I've opened an issue about this on GitHub, possible bug
Route re-rendering component on every path change, despite path of "/"
Found the reason this is happening straight from a developer (credit Tim Dorr). The route is re-rendering the component every time because it is an anonymous function. This happens twice down the tree, both in App and Routes (within Loadable function), below respectively.
<Route path="/" component={() => <Routes userRole={user.Role} />} />
needs to be
<Routes userRole={user.Role} />
and
loader: () => import('./systemAdminRoutes/SystemAdminRoutes')
Basically my whole approach needs to be rethought
EDIT: I eventually fixed this by using the render method on route:
<Route path="/" render={() => <Routes userRole={user.Role} />} />
Bumped into this problem and solved it like this:
In the component:
import {useParams} from "react-router-dom";
const {userRole: roleFromRoute} = useParams();
const [userRole, setUserRole] = useState(null);
useEffect(()=>{
setUserRole(roleFromRoute);
},[roleFromRoute]}
In the routes:
<Route path="/generic/:userRole" component={myComponent} />
This sets up a generic route with a parameter for the role.
In the component useParams picks up the changed parameter und the useEffect sets a state to trigger the render and whatever busines logic is needed.
},[userRole]);
Just put the "/" in the end and put the other routes above it.
Basically it's matching the first available option, so it matches "/" every time.
<Switch>
<Route key={key} exact path="/home" component={HomePage} />
<Route key={key} path="/second" component={SecondPage} />
<Route key={key} path="/third" component={ThirdPage} />
<Route key={key} exact path="/" component={HomePage} />
<Route key={key} component={NotFoundPage} />
</Switch>
OR
<Switch>
<Route path="/second" component={SecondPage} />
<Route exact path="/" component={HomePage} />
<Route path="*" component={NotFound} />
</Switch>
Reorder like this, it will start working.
Simple :)

Resources