Passing props to dynamically rendered components in React - javascript

I have a page that is displaying several of my star components that each have their own name and a prop called starType
I am generating several different of these stars with the following code
if (num > 0) {
return (
<div className="starWrapper">
<Star
name={`${makeid()}`}
starType={`${starList[Math.floor(Math.random() * 6 + 1)]} ${posList[Math.floor(Math.random() * 9 + 1)]}`}
></Star>
{makeStars((num - 1))}
</div>
);
And this is the star component
<NavLink to={props.name}>
<h1 className="star-label">{props.name}</h1>
<div className={``}>
<div className={`starBall ${props.starType}`}></div>
</div>
</NavLink>
At the moment I want the user to be able to click on each star and have it lead to a page. I have achieved that with react-router's dynamic routing
<Route
exact
path="/:id"
render={(props) => <GenerateSystem {...props} />}
/>
the issue is I want the page that is generated from my generateSystem component to have the starType prop passed to it by the star component. I am aware of React's one way data flow and I think that might be the issue. How can I pass prop data from an auto generated component to another auto generated component?
My full code is viewable here. The components I'm talking about are in the interstellar-view and systems folder.

since you are passing name through URL params so passing starType using query params is an easy option.
So URL would look like this www.example.com/123?starType=red-giant
In your star.jsx, make a modification like this
<NavLink to={`/${props.name}?starType=${props.starType}`}>
...
</NavLink>
In your App.js, make a modification like this
<Switch >
<Route exact path="/:id" component={GenerateSystem} />
<Route exact path="/sol" component={SolSystem} />
<Route exact path="/" component={Interstellar} />
</Switch>
(We do not need to render and pass props since we can use useParams in GenerateSystem.js)
In your GenerateSystem.js, make a modification like this
import React from "react";
import { Link, useLocation, useParams } from "react-router-dom";
function useQuery() {
return new URLSearchParams(useLocation().search);
}
export const GenerateSystem = (props) => {
const {name} = useParams();
const query = useQuery();
const starType = query.get('starType')
return(<div className={starType}>System <p>{name}</p></div>)
}
Refs:
https://reactrouter.com/web/api/Hooks/useparams
https://reactrouter.com/web/example/query-parameters
EDIT:
You can use Redux-store/Context-API to have a global store, so that name and starType can be stored globally and can be accessed in different components

More Use-cases Example -> for other people that came here:
As in React-Router-Dom V6-> there is no render method any more,
See Why does have an element prop instead of render or component?
We mentioned this in the migration guide from v5 to v6, but it's worth repeating here.
In React Router v6 we switched from using v5's and APIs to . Why is that?...
So I needed another way of dynamically rendering all routes for the Router, with a pre declared array with all routes:
const routingList = [{title: 'Home', search: '/', component: Home, icon: 'fa-home'},{...}]
<Routes>
{
routingList.map((routing) => {
let Child = routing.component;
return <Route key={routing.search} path={routing.search} element={<Child {...routing.compProps} />} />;
})
}
<Route path="*" element={<Notfound />} />
</Routes>
(BTW: if you also need the useLocation or the other hooks, and you are using React Class and not React functions, see my answer here:
Component with router props - For: Hooks can only be called inside of the body of a function component
)

Related

react-router v6 wrapped route component not working [duplicate]

This question already has answers here:
Error: [PrivateRoute] is not a <Route> component. All component children of <Routes> must be a <Route> or <React.Fragment>
(18 answers)
Closed last year.
How can you render a composed Route component
code example
Bottomline from above example is that in the following code, the Wrapped route will never render it's element
const App = () => (
<Routes>
<Wrapped/>
<Route path="/inline" element={<span >Inline works</span>} />
</Routes>
);
const Wrapped = () => <Route path="/wrapped" element={<span>wrapped</span>} />
Is there a way of doing this kind of composition with the Route component with react-router v6? Or will react-router v6 only support Route directly nested in the Routes component?
Edit, more specifically I'm trying to get a recommendation for using a ProtectedRoute component, something among the lines of:
type Props = {
element: ReactElement;
redirectRoute: string;
} & RouteProps;
const ProtectedRoute = ({element, redirectRoute, ...rest}: Props) => {
const {isAuthenticated} = useAuth();
<Route {...rest} element={isAuthenticated() ? element : <Navigate to={redirectRoute}/>}/>
}
EDIT:
It seems like this used to work in older beta versions, so this might be a bug. At the moment the latest version is 6.0.0-beta.4 &
I've logged an issue:
https://github.com/remix-run/react-router/issues/8066
In your code, you are trying to use Wrapped Component as a Router, but it's not. It's a React element returning a React Router element. Since you only need Router in this simple usecase, you can treat is as a function:
<Suspense fallback={null}>
<Routes>
{/* Use this as a normal function, and also function name etc.,*/}
{Wrapped()}
<Route path="/inline" element={<span>Inline works</span>} />
</Routes>
</Suspense>
However, I would recommend not to complicate the routes by trying to add customizations on route and instead wrap your component that you want to route.
For eg.,
const Wrapped = () => <Route path="/wrapped" element={<span>wrapped</span>} />;
to
<Route path="/wrapped" element={<Wrapped>wrapped component</Wrapped>} />
Or will react-router v6 only support Route directly nested in the Routes component?
Correct, RRv6 does not support route composition. Instead, try using your <Wrapped /> component inside the element prop. E.g.
<Route path="/foo" element={<Wrapped>/* something here */</Wrapped>} />

How can I conditionally render particular html sections depending on the Route

I'm using React for this project.
I have got a Help section on my website, This help section has about 50+ articles. If you go to /help-open-account I want to render some html and if I go to /fraud I want to display different html it is all dynamic content so its using the same js file hence why I want to render depending on the link.
I know how to use the conditional statements but how can I use the current url as a statement so like path="/(?!help--lostMyCard)" if its currently on that url then render the content this content instead of x content.
The app.js file has this following code ->
<BrowserRouter>
<Switch>
<Route path="/" exact component={HelpSupport} />
<Route to="/help-articles" component={HelpArticles} />
</Switch>
</BrowserRouter>
Would the code above be the only way to conditionally render content? As this is rendering full components and I wanted to conditionally render subcomponents within them.
Just to clarify, I know how to use the routes and so on, but thats not the question, the question is when you are in a component for example you are in the Help components, can I conditionally render content depending on my Route and if so how?
So the HelpfulArticles I want to render that if my route is X but if my route is Y then render this other component?
I see that you want to access route information in component.
It seems that what you are looking for has already been solved here
How to get current route in react-router 2.0.0-rc5
For more information -
Someone can club understanding with link here https://reactjs.org/docs/conditional-rendering.html in addition to above stackoverflow link i have mentioned in this comment to completely solve such problem.
if I understood correctly you want to add more routes based on your path:
Here below let's say help-articles has subpages that might have different HTML to render.
<BrowserRouter>
<Switch>
<Route path="/" exact component={HelpSupport} />
<Route path="/help-articles/:subpage?" component={HelpArticles} />
</Switch>
SO first to add subpage optional parameter.
Then in your HelpArticles component render method:
return {
const {match} = this.props;
const {params} = match;
if(!params.subpage) {
return <div> no subpage routes</div>
}else if(params.subpage === "fruits") {
return <FruitsHelper>
} else if(params.subpage === "veg") {
return <VegsHelper>
}
}
update the code here,
<BrowserRouter>
<Switch>
<Route path="/" exact component={HelpSupport} />
// not `to`, it is `path`
<Route path="/help-articles" component={HelpArticles} />
</Switch>
</BrowserRouter>
now, you can update HelpArticles component like below,
import React, { Component } from "react";
import List, { LostMyCard, LockedAccount } from "../custom/help";
class HelpArticles extends Component {
_renderArticle() {
const { location: { pathname } } = this.props;
// render the components by comparing the url, here you can use switch too.
if ((pathname || "").includes("lostMyCard")) {
return <LostMyCard />
}
if ((pathname || "").includes("locked-account")) {
return <LockedAccount />
}
// if no case is match then default component for listing the articles.
return <List />
}
render() {
return (
<React.Fragment>
{this._renderArticle()}
</React.Fragment>
)
}
}
export default HelpArticles;

React Router structuring - passing functions

I'm quite new to reactjs and was just wondering if there is any easy way to display information from the same component to different routes. In the following code as an example I have just two functions that are just returning divs full of text, and calling them and rendering them right away (in the class or in the router) would just have them be on the same "page".
I've tried passing the ref by props but they always ended up undefined. I figured a state change would be awkward since there is no real "event". I'm using create-react-app, react-routerv4, and react-bootstrap.
In App.js
import React, { Component } from 'react';
import './App.css';
import NavBar from './Components/NavBar/NavBar.js';
import Band from './Components/Text/Band.js';
import { Router, BrowserRouter, Link, Route } from 'react-router-dom';
class App extends Component {
render() {
return(
<div>
<BrowserRouter>
<div className="RenderRouter">
<Route exact path='/' component={NavBar}/>
<Route exact path='/' component={ControlledCarousel}/>
<Route exact path='/' component={Home}/>
//<Route exact path='/Artists/ArtistX' component={Band}/>
<Route exact path='/Artists/Artist1' component={NavBar}/>
<Route exact path='/Artists/Artist1' render={props => <Band band1text = {this.props.band1text} />}/>
<Route exact path='/Artists/Artist2' component={NavBar}/>
<Route exact path='/Artists/Artist2' render={props => <Band band2text = {this.props.band2text} />}/>
</div>
</BrowserRouter>
</div>
);
}
}
export default App;
In Band.js
import React, { Component } from 'react';
import './Band.css';
class Band extends Component {
//Constructor for state change would go here
band1text(props) {
return(
<div id="band1Text" className="BandText">
<h1>"The best riffs!</h1>
</div>
);
};
band2text(props) {
return(
<div id="band2Text" className="BandText">
<p>More info coming soon! Check out the interview!</p>
</div>
);
};
//Create handlers to call functions, and pass reference?
render() {
return(
<div className="BandDescription">
//calling in DOM render object, can't pass props from here?
//{this.props.band1text()} = compiler error
{this.band1text()}
{this.band2text()}
</div>
);
}
}
export default Band;
It would probably be easier to just have separate components and classes for every piece of each route (i.e, BandX.js, CarouselX.js) but that could get verbose and one would have to import many files. I'm using react to build a music player component for the app as well, that's why I'm not just using standard JS.
Try writing something like this in your Band component render:
render() {
return(
<div className="BandDescription">
{this.props.band1text && this.band1text()}
{this.props.band2text && this.band2text()}
</div>
);
}
This way it checks for the prop before running whichever method. If both methods are passed, both functions will return. And you shouldn't need to pass props to those methods. Try writing them as arrow functions so they will be bound band1text = () => { ... }, you will still be able to access this.props.band1text from inside the method.
The props would be undefined because there is no props with bandText being passed down to App component. Routes are nested in App component and this.props.band1Text means you are expecting to read from props passed to App. Try passing band1Text and band2Text as props to App component.
Also to read a props that's not a function just use {this.props.band1Text} in the Band.js component

How to get params in component in react router dom v4?

I have this code:
<BrowserRouter>
<Route path="/(:filter)?" component={App} />
</BrowserRouter>
the filter param or '' on the root is suppose to be on App components' props base on the previous react router versions?
This is my code on my App:
const App = ({params}) => {
return ( // destructure params on props
<div>
<AddTodo />
<VisibleTodoList
filter={params.filter || 'all'} // i want to git filter param or assign 'all' on root
/>
<Footer />
</div>
);
}
I logged this.props.match.params on console but it has none? help?
I assume you are following the Redux tutorial on Egghead.io, as your example code seems to use what is defined in that video series. I also got stuck on this part trying to integrate React Router v4, but eventually found a working solution not far from what you have tried.
⚠️ NOTE: one thing I would check here is that you are using the
current version of react-router-dom (4.1.1 at the time of this
writing). I had some issues with optional filters in the params on
some of the alpha and beta versions of v4.
First, some of the answers here are incorrect, and you indeed can use optional params in React Router v4 without the need for regular expressions, multiple paths, archaic syntax, or using the <Switch> component.
<BrowserRouter>
<Route path="/:filter?" component={App} />
</BrowserRouter>
Second, as others have noted, React Router v4 no longer exposes params on route handler components, but instead gives us a match prop to use.
const App = ({ match }) => {
return (
<div>
<AddTodo />
<VisibleTodoList filter={match.params.filter || 'all'} />
<Footer />
</div>
)
}
From here, there are two ways to keep your content in sync with the current route: using mapStateToProps or using the HoC withRouter, both solutions are already talked about in the Egghead series so I won't recapitulate them here.
If you are still having trouble, I urge you to check out my repository of the completed application from that series.
Here is the commit using the mapStateToProps solution
Here is the commit using the withRouter soluiton
Both of which seem to work fine (I just tested both of them).
React Router v4 does not accept a regex for the path. You won't find regular expressions covered anywhere in the documentation. Instead of a regex you can just create multiple routes inside the <Switch> component and the first one that matches will be rendered:
<BrowserRouter>
<Switch>
<Route path="/" component={App} />
<Route path="/:filter" component={App} />
</Switch>
</BrowserRouter>
You also have a bug in your App component. You get the params via the match property, not the params property (not tested, but this should be closer to what you want):
const App = ({match}) => {
return (
<div>
<AddTodo />
<VisibleTodoList
filter={match.params.filter || 'all'}
/>
<Footer />
</div>
);
}
All of the above is covered in the React Router V4 docs on ambiguous matches
From the react-router documentation, props.match.params is where your parameteres are stored.
So to access the filter name, try this
const App = ({match}) => {
...
<VisibleTodoList
filter={match.params.filter || 'all'}
/>
...
}
I know the question is about v4, but if sm looks for v5, we can use the useParams hook.
// /:filter
const {filter} = useParams();
To get the params in the path URL
//to your route component
<Route path={`${match.url}/upload/:title`} component={FileUpload}/>
=====
//Parent component
<Link to={`${match.url}/upload/css`}>
=====
//Child component
const {params: {title}} = this.props.match;
console.log(title);
result = css
I do not know why react router cannot detect my filter even though it's working properly, I resolved this problem by using location.pathname since it does the work for me. I think react router cannot detect my filter param because of regexp, I expected that so I put || to use all on root, but unfortunately for me I it cannot detect so I used location.pathname . I would appreciate suggestions on this since I am not sure with my path configuration on regexp.

react router this.props.location

I need a help with react-router v2+
I have to change class of navbar when route changed
for example for route /profile className will be "profile-header"
I tried to use this.props.location in navbar component but it shows undefined
Hope your help
Your navbar component (as you described it in your question) is probably not the route component, right? By route component I mean the one that you use in your react-router configuration that is loaded for a specific route.
this.props.location is accessible only on such route component, so you need to pass it down to your navbar.
Let's take an example:
Your router config:
<Router>
<Route path="/" component={App}>
// ...
</Router
Route component App:
class App extends React.Component{
// ...
render() {
return <Navbar location={this.props.location}/>
}
}
There could be a scenario where you may not have access to props.location to pass to the nav component.
Take for example - We had a header component in our project which was included in the routing switch to make it available to all routes.
<Switch>
<Fragment>
<Header/>
<Route path='..' component={...}/>
<Route path='..' component={...}/>
</Fragment>
</Switch>
In the above scenario there is no way to pass the location data to the Header component.
A better solution would be to us the withRouter HOC when a component is not being rendered by your router.
You will still have access to the router properties history, match and location when you wrap it in the withRouter HOC:
import { withRouter } from 'react-router-dom'
....
....
export default withRouter(ThisComponent)
react-router v4
From documentation:
<Route> component property should be used, when you have an existing component. <Route> render property takes an inline function, that returns html.
A <Route> with no path will always match.
Based on this, we can make a <Route> wrapper to any level of your html structure. It will always be displayed and have access to the location object.
As per your request, if a user comes to /profile page, the <header> will have profile-header class name.
<Router>
<Route render={({ location }) =>
<header className={location.pathname.replace(/\//g, '') + '-header'}>
// header content...
</header>
<div id="content"></div>
<footer></footer>
} />
</Router>
I couldn't solve it with the solutions given here and here is what worked for me:
I imported history into my component and assigned history.location.pathname to a variable which I later used for dynamic style manipulation.
In case you are rendering the component with pre-defined location.state values, first set your state with props.location.state then use your state data in your elements.

Categories