How to pass props to every instance of a reused component? - reactjs

Say I have a header component, which will be reused on every single page within my site. Is there a way to pass the same props to every instance of this Header component, without having to explicitly type out the props on every instance?
For example, suppose I have a home page and a /proposecourse page. Both of these pages have the "main" component, and also a header component, like so:
<Route path="/proposecourse">
<Header />
<Proposals />
</Route>
<Route exact path="/">
<Header
mysky={mysky}
loggedIn={loggedIn}
setLoggedIn={setLoggedIn}
setSkynetID={setSkynetID}
/>
<Home />
</Route>
As can be seen, the second instance of Header has many props passed to it. But the first Header, although the same component, does not have those props, and I cannot access them on that specific component. Is there an efficient way of passing the same props to both without retyping the code?

You can save the element to a local variable, and then use that multiple times in the rendering:
const Example = () => {
const header = (
<Header
mysky={mysky}
loggedIn={loggedIn}
setLoggedIn={setLoggedIn}
setSkynetID={setSkynetID}
/>
);
return (
<>
<Route path="/proposecourse">
{header}
<Proposals />
</Route>
<Route exact path="/">
{header}
<Home />
</Route>
</>
);
};

You can use a spread operator to avoid explicitly typing the props.
const myProps = {
mysky,
loggedIn,
setLoggedIn,
setSkynetID,
};
<Header {...myProps} />

Related

How to hide certain Components for some Routes [duplicate]

This question already has answers here:
How do I render components with different layouts/elements using react-router-dom v6
(2 answers)
Closed 10 months ago.
I want to hide Header and Footer Component in some routes, e;g for path="/invoice/:id/print"
I have an app with this type of layout using react-router-dom v5
<Router>
<Header />
<main className="py-0 px-0">
<div>
<Container fluid>
<Switch>
<Route path="/" component={HomeScreen} exact />
<Route path="/invoices" component={Invoices} exact />
<Route path="/invoice/:id/print" component={InvoicePrint} exact />
<Route path="/billing/invoice/create" component={InvoiceCreate} exact />
<Route path="*" exact>
<Error404 />
</Route>
</Switch>
</Container>
</div>
</main>
<Footer />
</Router>
The problem is if I go to
<Route path="/invoice/:id/print" component={InvoicePrint} exact />
Then Header and Footer also get rendered. But I want to hide them for this specific route. So how can I do that?
I'm using react-router version 5
That depends on how many pages should render the Header and Footer components.
If you want to render them on just a few pages, the simplest solution will be to move them to the components/pages where you'd like to render them.
If you'd like to hide them in just one or two places you can use the useRouteMatch hook.
You can also build some abstractions to simplify the readability a bit: let's make the Layout component, which will accept a prop (like renderHeaderAndFooter (you can change the name of course 😄)).
Layout component:
const Layout = ({ renderHeaderAndFooter, children }) => (
<div>
{renderHeaderAndFooter && (
<Header />
)}
{children}
{renderHeaderAndFooter && (
<Footer />
)}
</div>
)
and the place of usage:
const HomeScreen = () => (
<Layout renderHeaderAndFooter={true}>
// make stuff
</Layout>
)
const Invoices = () => (
<Layout renderHeaderAndFooter={false}>
// make stuff
</Layout>
)
simple way is to trigger your current route by using useRouteMatch in every component you want like:
const match = useRouteMatch("/invoice/:id/print"); // import { useRouteMatch } from "react-router-dom";
return !!match ? <></> : <Footer/> //or Header
check this
You have to update the Header and Footer components by adding a listener during the rendering phase of the component : componentDidMount()
Fetch the called component :
const shouldHide = useRouteMatch("/invoice/:id/print") || useRouteMatch("/other/route");
return (
!shoudlHide && <Header />
)

Update state variable within Router

I'm currently working on a React site, and I have a custom HeaderBar component, and I would like to display the current page title on it. I had planned to just update a state variable from the Router to change the title. However, understandably, this results in a "too many re-renders" error.
Here's my current code:
function App() {
const [pageTitle, setPageTitle] = useState("")
return (
<div>
<Router>
<HeaderBar siteTitle="My Website Name" pageTitle={pageTitle} />
<Switch>
<Route path="/login">
{setPageTitle("Login")}
<h1>Login Page</h1>
</Route>
<Route path="/main">
{setPageTitle("Main")}
<h1>Main Page</h1>
</Route>
<Route path="/about">
{setPageTitle("About")}
<h1>About Page</h1>
</Route>
</Switch>
</Router>
</div>
);
}
Obviously I could just move the HeaderBar declaration inside each of the Routes, but this seems like a hackish solution to me.
Is there a better way to go about this?
When calling functions inside JSX, these functions are executed on every render, which would results on the 3 setPageTitle calls being executed on every render (although in your case, only the first setPageTitle is going to be called thus resulting in the too many updates as it would be cycling between calling the first setPageTitle, updating the state, rendering again, and calling it once again).
Ideally, if you define components for each route, you would need to call setPageTitle outside of the return statement (meaning outside of the JSX, before rendering), which would result in the setPageTitle being called only when you access the corresponding route.
In order to do that without defining components for your routes, you could do the following:
function App() {
const [pageTitle, setPageTitle] = useState("")
return (
<div>
<Router>
<HeaderBar siteTitle="My Website Name" pageTitle={pageTitle} />
<Switch>
<Route path="/login" render={() => {
setPageTitle("Login");
return (<h1>Login Page</h1>);
}}>
</Route>
<Route path="/main" render={() => {
setPageTitle("Main");
return (<h1>Main Page</h1>);
}}>
</Route>
<Route path="/about" render={() => {
setPageTitle("About");
return (<h1>About Page</h1>);
}}>
</Route>
</Switch>
</Router>
</div>
);
}

How to pass data from functional component to class component in reactjs?

I want to pass data from my App.js file to a sub class component.I have tried with props,but its not workng at all
const org = 'Organization Dashboard'
const remotes = 'FromAdmin';
const DashboardContainer = ()=>(
<Sidebar org={org}>
<div className="App">
<Route path='/dashboard' component={Dashboard}/>
<Route path='/mytab' component={MyTab}/>
<Route path='/team' component={MyTeam}/>
<Route path='/settings' component={Settings}/>
<Route path='/logout' component={Logout}/>
<Route path='/team-settings/:param1' component={TeamSettings}/>
**<Route remotes='remotes' path='/user-settings/:param1' component={MyTab}/>**
</div>
</Sidebar>
)
I want to pass data in MyTab class component, when i use this.props in myTab , its showing undefined
Help will be appreciated
I assume you're trying to pass remotes='remotes' to MyTab. Components rendered by a Route are passed only the route props, but you can use an anonymous inner function to slip in extra props. Don't forget to pass on the route props if you need them!
<Route
path='/user-settings/:param1'
component={routeProps => <MyTab {...routeProps} remotes='remotes' />}
/>
You can override the component. By default, component accepts a class-based or function-based component. But you can override the component. Not only data you can pass functions as well. But you should not do this. Use redux to achieve this kind of thing. If it is static data then you can pass this way. But id it is dynamic data then use redux instead.
<Route
path = '/myTab'
component = {(props) => <MyTab {...props} data={data}/>}
/>

React pass an object to the route

I have this set up of my app in the ´app.js´ file I have my routers defined like this inside the render method
<HashRouter>
<StatusToast />
<Switch>
<Route
exact={true}
path="/"
render={props => (
<ImageScene{...props} title="Images & Information Search" />
)}
/>
<Route path="/case/:id/images" component={DetailsScene} />
</Switch>
</HashRouter>
from ImageScene on a table row click, I call a method like this:
this.props.history.push(/case/${id}/images)
this trigger a route and load DetailsScene where I can get the passed in id like this this.props.match.params.id all works without any problem so far.
My question is how can I pass a more than a string (the id) can I pass somehow the whole object to the route?
I have tried to do something like this for 2nd route instead of:
<Route path="/case/:id/images" component={DetailsScene} />
to set up on the ImageScene a method which can expose the selceted object, for now lets just do a simple one like:
export function getSelectedRow() {
return {
title: 'test'
}
}
and than set up the route like:
const object = getSelectedRow();
<Route path="/case/:id/images"
render={props => (<DetailsScene{...props} title={object.title} />
)}
/>
but I cannot make it work... any help would be graet, I'm totally new to react and the whole router.
you could add state to history.push (https://reacttraining.com/react-router/web/api/history) which is available for component being rendered (DetailsScene). Remember to wrap DetailsScene withRouter(...) to have history.location.state available in its props.

React passing props down through switch element

I'm having an issue passing props through React elements (like Switch and Route). In the example below, I would like to pass all props of Dashboard component down to the Account component. Is there a way to achieve this?
App.js
<Dashboard>
<Switch>
// Dashboard props to Account component
<Route path="/account" render={props => <Account {...props} /> } exact />
<Route path="/someothercomponent" component={Someothercomponent} />
</Switch>
</Dashboard>
Dashboard.js
render() {
const children = React.Children.map(this.props.children, child => {
var router = React.cloneElement(child, { image: this.state.image });
return router;
// Like this the router Element does receive the image prop from
// the Dashboard component. Now this image prop needs to be
// passed on to the Account component.
}
I like some of the answers already present. To give you a sense of solving this problem differently and also something to learn and add to your toolbox. I would say use Context. Context provides a way to pass data through the component tree without having to pass props down manually at every level. https://reactjs.org/docs/context.html
So if you get to your Account and have to yet again pass props down this might be a good place to implement this.
When setting up correctly you could do something like this on your page. But again you aren't just passing down one you are passing down all props. And then what if you need to also pass them down on the next component <<< this is the point of Context. I would think using context is better than using a component as your state considering a stateful component is usually limited. With context, your Account component could have several children and you wouldn't have to pass props all the way down to get done what you wish to achieve.
<AppContext.Consumer>
{({prop1, prop2, prop3}) => {
}}
</AppContext.Consumer>
That's assuming you name your variable AppContext when you use React.createContext();
The idea is that passing down props at many levels can be annoying for some but using context you can bring a property in at any time without having to worry about if you passed them down correctly. Be sure to read the article in full there are times where you want to use context and times where you do not.
Yes, use render property instead.
<Route path="path" render={() => <MyComponent {...this.props} />} />
The problem is component is overriding the render props.
Remove component={Account}
I've also added brackets around (props) to improve readability
<Dashboard>
<Switch>
<Route
path="/account"
render={(props) => <Account {...props} /> }
exact
/>
<Route
path="/someothercomponent"
component={SomeOtherComponent}
/>
</Switch>
</Dashboard>
Alternatively:
const renderMergedProps = (component, ...rest) => {
const finalProps = Object.assign({}, ...rest);
return( React.createElement(component, finalProps)
);
}
const PropsRoute = ({ component, ...rest }) => {
return (
<Route {...rest} render={routeProps => {
return renderMergedProps(component, routeProps, rest);
}}/>
);
}
<Router>
<Switch>
<PropsRoute path='/login' component={Login} auth={auth} authenticatedRedirect="/" />
<PropsRoute path='/trades' component={Trades} user={user} />
</Switch>
</Router>
source

Resources