Accessing parent state in child in React - javascript

I have (e.g.) two components in React. The first, app.js, is the root component. It imports some JSON data and puts it in its state. This works fine (I can see it in the React devtools).
import data from '../data/docs.json';
class App extends Component {
constructor() {
super();
this.state = {
docs: {}
};
}
componentWillMount() {
this.setState({
docs: data
});
}
render() {
return (
<Router history={hashHistory}>
<Route path="/" component={Wrapper}>
<IndexRoute component={Home} />
<Route path="/home" component={Home} />
<Route path="/docs" component={Docs} />
</Route>
</Router>
);
}
}
The second, docs.js, is meant to show this JSON data. To do that it needs to access the state of app.js. At the moment it errors, and I know why (this does not include app.js). But how then can I pass the state from app.js to docs.js?
class Docs extends React.Component {
render() {
return(
<div>
{this.state.docs.map(function(study, key) {
return <p>Random text here</p>;
})}
</div>
)
}
}

The proper way of doing this would be by passing state as props to Docs component.
However, because you are using React Router it can be accessed in a bit different way: this.props.route.param instead of default this.props.param
So your code should look more or less like this:
<Route path="/docs" component={Docs} docs={this.state.docs} />
and
{this.props.route.docs.map(function(study, key) {
return <p>Random text here</p>;
})}

Another way of doing this is:
<Route path="/docs" component={() => <Docs docs={this.state.docs}/>}>
If you need to pass children:
<Route path="/" component={(props) => <Docs docs={this.state.docs}>{props.children}</Docs>}>
If you are doing it like this, then you can access your props values directly by this.props.docs in Child Component:
{
this.props.docs.map((study, key)=> {
return <p key={key}>Random text here</p>;
})
}

Another way of doing this will be
<Route path='/' render={ routeProps => <Home
{...routeProps}
docs={this.state.docs}
/>
}
/>
While in the child component you can access docs using
this.props.docs
Hope it helps!

Related

React Router v5: Route defined in child component not working

My application is using react-router-dom v5.3 and I'm having trouble routing from the root url of my application to a child component (called the "See All" Page) while also passing props down. Currently, my code just renders an empty page whenever I navigate to the child component.
RootRouter.js:
export default function RootRouter() {
return (
<BrowserRouter>
<Switch>
<Route
path="/"
exact
render={() => <HomeView />}
/>
</Switch>
</BrowserRouter>
);
}
Homeview.js:
function HomeView() {
const seeAllViewTitle = "some_title_here"
return (
<div>
<div>Some content here!</div>
<Link to={`/seeall/${seeAllViewTitle}`}}>
<Button/>
</Link>
<Route path={`/seeall/${seeAllViewTitle}`}>
<SeeAllView
groupTitle={""}
pageData={[]}
eventHandler={some_function_here}
/>
</Route>
</div>
);
}
If I were to put the Route that is currently in homeview.js inside of Rootrouter.js, the component shows up, but I can't pass any props into it from there.
Issue
The HomeView component is rendered only when the path is exactly "/". When the link is clicked and navigates to "/seeall/some_title_here " the path no longer exactly matches and the HomeView component unmounts.
Solution
Remove the exact prop from the root route so nested/sub routes can also be matched and rendered.
export default function RootRouter() {
return (
<BrowserRouter>
<Switch>
<Route path="/" component={HomeView} />
</Switch>
</BrowserRouter>
);
}
If you did not intend for these components to be rendered at the same time then move the nested route out to the RootRouter component.
export default function RootRouter() {
return (
<BrowserRouter>
<Switch>
<Route path="/seeall/:title">
<SeeAllView
groupTitle={""}
pageData={[]}
eventHandler={some_function_here}
/>
</Route>
<Route path="/" component={HomeView} />
</Switch>
</BrowserRouter>
);
}
...
function HomeView() {
const seeAllViewTitle = "some_title_here"
return (
<div>
<div>Some content here!</div>
<Link to={`/seeall/${seeAllViewTitle}`}}>
<Button/>
</Link>
</div>
);
}
Are you remembering to receive props in the function declaration for HomeView? usually, you'll need to explicitly define that you are receiving props, either with a props variable or by defining specific prop names in an object syntax

Confused between the difference between JSX.Element vs React.Component and when to use them with React-dom-router

I am starting out a new react-typescript project and I am using react-router-dom to manage routing but I am running into the following error in the browser counter:
Matched leaf route at location "/restaurant" does not have an element. This means it will render an <Outlet /> with a null value by default resulting in an "empty" page
Here's the code:
<BrowserRouter>
<SiteNav hasItemInCart={false} numberOfItemsInCart={0}/>
<Routes>
<Route path="/home" children={Home} />
<Route path="/restaurant" children={Restaurant} />
<Route path="/hostel" children={Hostel} />
</Routes>
</BrowserRouter>
export default class Home extends Component<any, any> {
render() {
return (
<>
<div>
Home
</div>
</>
)
}
}
class Hostel extends React.Component<any, any> {
render() {
return (
<div className="Hostal">
<body>Hostal</body>
</div>
);
}
}
As I understand it I am suppose to return a JSX.element but typescript is throwing errors when I try to use it. Why can't I use React.Component? How should I do this instead?
JSX.Element is the type of already rendered JSX. For example:
const a: JSX.Element = <>asd</>
const b: JSX.Element = <SomeComponent />
React.Component is the type of a component that knows how to produce a JSX.Element when rendered with specific props.
function A() { return <>Testing</> } // A is a component
const a: JSX.Element = <A />
Typically, children in React is a JSX.Element that you pass as the contents of a tag.
<A>
<div>children go here</div>
</A>
Or you can use the children prop explicitly:
<A children={<div>children go here</div>} />
So in your case, you are passing a component to a prop that expects rendered content.
If you want to pass in rendered content, you want either:
<Route path="/home" children={<Home />} />
Or:
<Route path="/home"><Home/></Route>
Though modern version of react router recommend passing JSX to the element prop. So use this unless you're on an older version.
<Route path="/home" element={<Home />} />
I think you are mixing up React types and the Route component API. In react-router-dom#6 there is now only an element prop (no component or render/children function props) taking a React.ReactNode, .a.k.a. any valid JSX.
Routes and Route
declare function Route(
props: RouteProps
): React.ReactElement | null;
interface RouteProps {
caseSensitive?: boolean;
children?: React.ReactNode;
element?: React.ReactNode | null;
index?: boolean;
path?: string;
}
You are passing your routed components to the Route using the RRDv5 Route component API/syntax. Convert your routes to use the element prop.
Example:
<BrowserRouter>
<SiteNav hasItemInCart={false} numberOfItemsInCart={0} />
<Routes>
<Route path="/home" element={<Home />} />
<Route path="/restaurant" element={<Restaurant />} />
<Route path="/hostel" element={<Hostel />} />
</Routes>
</BrowserRouter>
What version of react-router-dom is in your package.json?
This could be a versioning problem, on react-router-dom v6 all the routes are paired like this
[props.path]: [props.element]
That means, for example, for rendering a "hey" in a "/" path
<Route path="/" element={<div>Hey!</div>} />
In your case, you'd have to invoke for restaurants your <Restaurants /> component
<Route path="/" element={<Restaurants />} />

Passing props in React Route component for "react-router-dom": "^5.2.0"

I'm using "react-router-dom": "^5.2.0".
I simply want to pass props to Child components Like this
note: this is pseudo-code:
<Route exact path="/databases/buyordersdata" component={BuyOrders param1={value1}} />
This is the React file:
class Databases extends Component {
render() {
return (
<div>
<BrowserRouter>
<Switch>
<Route exact path="/databases/" component={Navigation} />
<Route exact path="/databases/makebuy" component={MakeBuy} />
<Route exact path="/databases/makesell" component={MakeSell} />
<Route exact path="/databases/buyordersdata" component={BuyOrders} />
<Route exact path="/databases/sellordersdata" component={SellOrders} />
<Route exact path="/databases/AllMyOrders" component={MyOrders} />
</Switch>
</BrowserRouter>
</div>
)
}
}
const element = <Databases />;
ReactDOM.render(element, document.getElementById('contents'));
Solution
In parent component
<Route exact path="/databases/buyordersdata" render={
(props) => <BuyOrders {...props} order_type={true}/>
}/>
then in Child component
class BuyOrders extends Component {
constructor(props){
super(props)
}
...
render() {
console.log(this.props.order_type)
You just do something like this: Instead of using the route component like that, you can use the Route component as a parent and the component itself as a child.
<Route exact path="/path">
<BuyOrders param1={value1} /> // Then pass the props like that
</Route>

Passing function prop to child component (via Link and Route components)

I'm using pure React with a Rails API backend.
I am fetching data from API and storing state in my Trips component. I have a Link component where I am able to pass the state to my NewTrip component, however <Link> does not allow me to pass functions.
I am able to pass functions to NewPage via render method on the Route component located at './routes/Index'.
But how do I pass the function from my Trips component? It's so much easier when passing as props to the component, the Router seems to be in the way!
'routes/Index.js'
export default (
<Router>
<Switch>
<Route path="/" exact component={Home} />
<Route path="/trips" exact component={Trips} />
<Route path="/trip" render={(routeProps)=><NewTrip {...routeProps} />}/>
<Route path="/trip/:id" exact component={Trip} />
<Route path="/trip/:id/cost" exact component={NewCost} />
<Route path="/trip/:id/edit" exact component={EditTrip} />
</Switch>
</Router>
);
'components/Trips'
class Trips extends React.Component {
constructor(props) {
super(props);
this.state = {
trips: []
}
this.addTrip = this.addTrip.bind(this);
}
addTrip(trip) {
const {trips} = this.state;
trips.push(trip);
this.setState({ trips: trips});
}
render(){
return(
<Link
to={{
pathname: "/trip",
state: {trips: trips, onAddTrip={this.addTrip}} // not allowed,
// but I want to pass this function to the
// Component which is rendered by the Route in Index.js
}}
className="btn custom-button">
Create New Trip
</Link>
)
}
}
I think you should lift state up of the Trips component and have it in your 'routes/Index.js'. (it will need to be a component now, not just an export).
'routes/Index.js'
export default class Routes extends React.Component {
constructor(props) {
super(props);
this.state = {
trips: []
}
this.addTrip = this.addTrip.bind(this);
}
addTrip(trip) {
const {trips} = this.state;
trips.push(trip);
this.setState({ trips: trips});
}
render() {
return (
<Router>
<Switch>
<Route path="/" exact component={Home} />
<Route path="/trips" exact component={Trips} />
<Route path="/trip" render={(routeProps)=>
<NewTrip addTrip={this.addTrip} trips={this.state.trips} {...routeProps} />
}/>
<Route path="/trip/:id" exact render={(routeProps)=>
<Trip addTrip={this.addTrip} trips={this.state.trips} {...routeProps} />
}/>
<Route path="/trip/:id/cost" exact component={NewCost} />
<Route path="/trip/:id/edit" exact component={EditTrip} />
</Switch>
</Router>
);
}
}
'components/Trips'
class Trips extends React.Component {
render() {
const trips = this.props.trips
return (
<Link
to={{
pathname: "/trip",
}}
className="btn custom-button">
Create New Trip
</Link>
)
}
}
It might be better to have the state even higher up in the App component, but you didn't provide that, so this has to do :)
You can pass functions using state in react router Link.
<Link
to={{
pathname: "/trip",
state: {trips: trips, onAddTrip: this.addTrip}
}}
className="btn custom-button">
Create New Trip
</Link>
And then in /trip, you retrieve and use the function like this:
this.props.location.state.addTrip();

withRouter Does Not passes Route props to component

Here is my navigation component:
import React from 'react'
class Navigation extends React.Component {
constructor(props) {
super(props);
this.state = {
type: 'signUp', // or login
showModal: false,
isLoggedIn: false,
}
}
...some code
render() {
const { showModal, type, isLoggedIn } = this.state
console.log(this.props.location); // all problem is this, I'm not getting it in console
return(
...some more code
)
}
}
export default withRouter(Navigation)
And here is where it it been used in app.js
class App extends React.Component {
render () {
return(
<Router>
<Fragment>
<Navigation /> // <= right there
<Switch>
<Route exact path='/' component={HomePage}/>
<Route exact path='/search' component={HomePage}/>
<Route component={Lost} />
</Switch>
</Fragment>
</Router>
)
}
}
I want to get updated route props like match and location and history in my <Navigation /> component but I get it only when the first time that component mounts on the DOM, in my other components I update the route using window.history.pushState but I am not able to get route props from withRouter after link in the browser is been updated.
I update route with window.history.pushState because:
I could not find any way to update just link in the address bar without showing user or redirecting user to new component with React router DOM (am I doing it in right way or not?)
based on that I then use window.location.pathname to add some specific stylings to some components)
Also, I read the entirety of this and this but I could not solve this issue. What am I doing wrong?
withRouter gives you the closest <Route>'s route props, and since the Navigation component is not inside a Route you will not get the route props.
You could e.g. put the Navigation component on a Route outside of the Switch that will always be visible.
Example
class App extends React.Component {
render() {
return (
<Router>
<Fragment>
<Route path="/" component={Navigation} />
<Switch>
<Route exact path="/" component={HomePage} />
<Route exact path="/search" component={HomePage} />
<Route component={Lost} />
</Switch>
</Fragment>
</Router>
);
}
}

Categories

Resources