React Router Dom Not Passing State As Component - reactjs

I am trying to pass a symbol in the state so that I can render a slug page based on the prop. I am havving issues passing the state in as it breaks everything. I am using react-router-dom to navigate the pages. I have a Link in another file to connect the slug pages.
App.js
function App() {
return (
<div>
<AuthContextProvider>
<Navbar/>
<Routes>
<Route path='/' element={<Home/>}/>
<Route path='/showinvestments' element={<Protected><ShowInvestments/></Protected>}/>
<Route path='/addinvestments' element={<Protected><Add/></Protected>}/>
<Route path='/sell/:stockSell' element={<Protected><Sell ticker={this.state.symbol}/></Protected>}/>
<Route path='/login' element={<Login/>}/>
</Routes>
</AuthContextProvider>
</div>
);
}
The Link to the slug page
....
<div className='col text-center d-flex ' >
<Link to={{pathname: sellLink, state: {symbol: ticker}}}>
<AiFillEdit className='mt-2'/>
</Link>
</div?
....
The page that I am trying to render based on the props passed in
const Sell = ({ticker}) => {
// const {state} = useLocation();
// const { tic } = state;
// console.log(tic);
return (
<div className='container'>
Hello
{ticker}
</div>
)
}
Any help would be appreciated.
I have tried different variants inside of <Route>. If I pass a string as the ticker, it prints that, I just cannot get it to pass the current state in.

Related

React: Private Routes Using Outlet & Router V6

I'm currently creating private routes using react-router 6 and Outlet. I have a simple setup as follows:
Routes in App.js
return (
<BrowserRouter>
<div className="App">
<Routes>
<Route path="/" element={<Login />}></Route>
<Route path="/register" element={<Register />}></Route>
<Route element={<PrivateRoutes />}>
<Route path="/dashboard" element={<Dashboard />}></Route>
<Route path="/reports" element={<Reports />}></Route>
<Route path="/settings" element={<Settings />}></Route>
</Route>
</Routes>
</div>
</BrowserRouter>
);
PrivateRoutes component
const PrivateRoutes = () => {
console.log("Private Routes was run");
validToken = true;
return (
(checkToken()) ? <Outlet/> : <Navigate to="/" />
);
}
What I noticed though is the PrivateRoutes component does not run when navigating between any of the private routes, only when first entering one of the PrivateRoutes.
For example, I want the PrivateRoutes component to run when I navigate between the /dashboard and /reports routes using my primary nav.
I tried using both Link and useNavigate imports from react-router-dom, but neither re-triggers the PrivateRoutes component.
<div id='nav-links' className='flex flex-row'>
<div onClick={() => navigate('/dashboard')}>Dashboard</div>
<div onClick={() => navigate('/reports')}>Reports</div>
<div onClick={() => navigate('/settings')}>Settings</div>
</div>
and
<div id='nav-links' className='flex flex-row'>
<Link to='/dashboard'>Dashboard</Link>
<Link to='/reports'>Reports</div>
<Link to='/settings'>Settings</div>
</div>
What am I missing or not understanding? Is there a simple syntax adjustment one can use so PrivateRoutes is always run upon navigation?
Your routes are fine. You will want to couple the checkToken to the route change. You can listen to route changes using the useLocation and useEffect hooks. I'm assuming checkToken is synchronous.
Example:
const PrivateRoutes = () => {
const { pathname } = useLocation();
const [isValidToken, setIsValidToken] = useState(); // <-- initially undefined
useEffect(() => {
// initial mount or route changed, check token
setIsValidToken(!!checkToken());
}, [pathname]);
if (isValidToken === undefined) {
return null; // or loading indicator/spinner/etc
}
return isValidToken ? <Outlet/> : <Navigate to="/" replace />;
}

React. Cannot get URL params with useParams() in nested component

I have a simple demo application where you can view quotes individually (QuoteDetail) and view comments under them. I need the url parameters in both components, but I can only get them in the parent component.
App.tsx:
<Switch>
// ...
<Route path="/quotes" exact>
<AllQuotes />
</Route>
<Route path="/quotes/:quoteId">
<QuoteDetail />
</Route>
// ...
</Switch>
QuoteDetail.tsx:
export interface QuoteDetailParams {
quoteId: string;
}
const QuoteDetail: React.FC<QuoteDetailProps> = (props) => {
const match = useRouteMatch();
const { quoteId } = useParams<QuoteDetailParams>();
// ...
console.log(quoteId); // <-- THIS ONE WORKS
// ...
return (
<React.Fragment>
{quote}
<Route path={match.path} exact>
<div className="centered">
<Link className="btn--flat" to={`${match.url}/comments`}>
Show Comments
</Link>
</div>
</Route>
<Route path={`${match.url}/comments`} exact>
<div className="centered">
<Link className="btn--flat" to={`/quotes/${quoteId}`}>
Hide Comments
</Link>
</div>
<Comments />
</Route>
</React.Fragment>
);
};
As you can see, when I click a button to load comments, the url changes from /quotes/1/ to /quotes/1/comments which should load the Comments component as well. However, in Comments.tsx, I can't access the url parameter.
Comments.tsx:
const Comments = () => {
const params = useParams<QuoteDetailParams>();
const { quoteId } = params;
// ...
console.log(params); <-- THIS ONE RETURNS undefined
// ...
return (
<section className={classes.comments}>
<h2>User Comments</h2>
{comments}
</section>
);
};
I have no idea what I'm doing wrong here. Of course, I can pass the parameter using a component prop as well, but this is not desired in my case. Help is appreciated.
I'm using react-router-dom#5.3.0
match
match.url is the portion of the URL, it's useful for building nested <Link>s.
match.path is the path pattern used to patch, it's useful for building nested <Route>s.
Your QuoteDetail component is using match.url for building the nested route when it should use match.path.
<Route path={`${match.url}/comments`} exact> // <-- incorrect route path
...
</Route>
It should be:
<Route path={`${match.path}/comments`} exact>
...
</Route>

Cannot render child components with React Router (Nested Routes)

I am trying to use nested routes to render different components. When I click my links, URL does update but the components are not rendering. Prior to this I was using imported components, but since that wasn't working, I stripped it down to this block of code and it's still just showing a blank component and no errors.
import React from 'react';
import { Route, Switch, Link, useRouteMatch } from 'react-router-dom';
function InfluencerComponent() {
let { path, url } = useRouteMatch();
const navLinks = (
<div>
<Link to={`${url}/select-trade`}>Select trade</Link>
<Link to={`${url}/add-skills`} className="ml-2">
Add skills
</Link>
</div>
);
return (
<div className="row mt-3">
<Switch>
<Route exact path={path}>
{navLinks}
</Route>
<Route path={`${path}/select-trade`}>
{navLinks}
<Test />
</Route>
<Route path={`${path}/add-skills`}>
{navLinks}
<TestTwo />
</Route>
</Switch>
</div>
);
}
function Test() {
return 'Test Component';
}
function TestTwo() {
return 'Another Test Component';
}
export default InfluencerComponent;
Components are not rendering because you should use component prop instead of children.
Example:
return (
<div className="row mt-3">
<Switch>
// ...
<Route path={`${path}/add-skills`} component={<>{navLinks}<TestTwo /></>} />
</Switch>
</div>
);
More info about <Route /> props:
https://reacttraining.com/react-router/web/api/Route/component

React Router: Route defined in child component not working

I'm working on a React web application using React router.
The Route objects defined on the main wrapper component are working just fine, but if I try to define a Route on a child component, any link pointing to it won't be able to render the desired component.
Here is a code snippet trying to explain the situation:
class MainWrapper extends Component {
render() {
return (
<div className="App">
<Switch>
<Route exact path="/a" component= {A}/>
</Switch>
</div>
);
}
}
const A = () => {
return (
<div>
<Route exact path="/b" component={B}/>
<Link to="/b"/>
</div>
)
}
const B = () => {
return (<div>HELLO</div>)
}
In my application, the link pointing to "/b" is not rendering the B component, like the component prop weren't passed
Why this won't work?
You are specifying "exact path" in both Routes, so for rendering B your path should be exactly "/b", but when linking to "/b" component A will unmount because for rendering A you must be on exact path "/a". You should change your approach. One would be removing "exact" and including "/a" to your Link:
class MainWrapper extends Component {
render() {
return (
<div className="App">
<Switch>
<Route path="/a" component= {A}/>
</Switch>
</div>
);
}
}
const A = () => {
return (
<div>
<Route path="/b" component={B}/>
<Link to="/a/b"/>
</div>
)
}
const B = () => {
return (<div>HELLO</div>)
}
if B is a child of A, the url should be /a/b instead of /b, so you just need to update the A component with this code
const A = ({match}) => {
return (
<div>
<Route exact path={`${match.url}/b`} component={B}/>
<Link to=to={`${match.url}/b`}/>
</div>
)
};
See the documentation here
Do you have a Router somewhere? Also, you haven't closed your Link tag.
You need to wrap it in a Switch, and you should remove the exact prop from your /b route.
const A = ({match}) => {
return (
<div>
<Switch>
<Route path={`${match.url}/b`} component={B}/>
</Switch>
<Link to="a/b"/>
</div>
)
}

React Router: Get location in container component

I need to access the active path in the parent component, so that I can vary some CSS depending on the location. Here is App - the parent:
class App extends React.Component {
render(){
return (
<div className="wrapper" >
<div className="content">
<Router>
<div className="container_b">
<Menu />
<div>
<Route exact path="/" component={Home}/>
<Route path="/confidence" component={ConfidenceInterval}/>
<Route path="/proind" component={ProportionTestInd}/>
<Route path="/prodep" component={ProportionTestDep}/>
</div>
</div>
</Router>
<div className="push"></div>
</div>
<footer className="footer"><Footer /></footer>
</div>
)
}
}
I think for the App component, you have two options. First one is wrapping App with withRouter higher order component.
React-router provides a higher order component called withRouter, which passes match, history and location props to the component wrapped by it.
(for details please check withRouter)
In that case you can to the following
class App extends React.Component {
render(){
const {location: {pathname}} = this.props // pathname gives you the current path
return (
<div className="wrapper" >
<div className="content">
<Router>
<div className="container_b">
<Menu />
<div>
<Route exact path="/" component={Home}/>
<Route path="/confidence" component={ConfidenceInterval}/>
<Route path="/proind" component={ProportionTestInd}/>
<Route path="/prodep" component={ProportionTestDep}/>
</div>
</div>
</Router>
<div className="push"></div>
</div>
<footer className="footer"><Footer /></footer>
</div>
)
}
}
export const WrappedApp = withRouter(App)
Second option is, (again for App component) you can use window.location (please check window.location). location.pathname should also give you the current path in that case.
For other components, like Home,ConfidenceInterval etc, Route passes match, history and location props by default so you can make use of them.
import {useLocation} from 'react-router-dom'
const App = () => {
const location = useLocation();
console.log(location.pathname);
enter code herereturn <span>Path : {location.pathname}</span>
}

Resources