I have 3 React components with such relationship:
I want to have a button in ChildofChild
component, when clicked to update state in Parent
component. I can send it to Child
component via props and run a fuction there.
ChildOfChild
// ChildOfChild Component
export class PlaceInfoWindow extends Component {
render() {
const {description, name, price} = this.props
return(
<InfoWindow onCloseClick={this.props.closeWindow}>
<div>
<h1>{name}</h1>
<p>{description}</p>
<span>${price}</span>
<button type="button" onClick={this.props.onAdd} className="btn btn-primary btn-success m-2">Add</button>
</div>
</InfoWindow>
);
}
}
export default PlaceInfoWindow
Child
//Child Component
export class PlaceMarker extends Component {
constructor(props) {
super(props);
this.state = {
showTooltip: false,
};
}
clickTooltip() {
this.setState({ showTooltip: !this.state.showTooltip });
}
closeWindow() {
this.setState({ showTooltip: false });
}
render() {
const { showTooltip } = this.state;
const { lat, lng, name, price, description } = this.props;
return (
<Marker
position={{
lat: parseFloat(lat),
lng: parseFloat(lng)
}}
onClick={this.clickTooltip.bind(this)}
icon="https://image.ibb.co/cGPSW8/red_marker.png"
>
{showTooltip && (
<PlaceInfoWindow
description={description}
name={name}
price={price}
closeWindow={this.closeWindow.bind(this)}
onAdd={this.props.onAdd}
/>
)}
</Marker>
);
}
}
export default PlaceMarker;
Parent
// Parent Component
const AirbnbMap = withGoogleMap(props => (
<GoogleMap
defaultCenter={props.center}
defaultZoom={props.zoom}
defaultOptions={{
styles: userMapStyle
}}
>
{props.places.length > 0 &&
props.places.map(place => (
<PlaceMarker
key={`place${place.id}`}
id={place.id}
lat={place.latitude}
lng={place.longitude}
description={place.description}
name={place.name}
price={place.price}
onAdd={this.handleAdd}
/>
))}
</GoogleMap>
));
export class Map extends Component {
constructor(props) {
super(props);
this.zoom = 7;
this.state = {
lat: 50.0515918,
lng: 19.9357531,
places: [
{
id: 1,
latitude: 50,
longitude: 20,
description: "ABC",
name: "City",
price: 20
}]
};
}
handleAdd = () => {
console.log("handle add called");
};
render() {
const { lat, lng, places } = this.state;
console.log(places);
return (
<div style={{ width: `100%`, height: `750px` }}>
<AirbnbMap
center={{
lat: lat,
lng: lng
}}
places={places}
zoom={this.zoom}
containerElement={<div style={{ height: `100%` }} />}
mapElement={<div style={{ height: `100%` }} />}
/>
</div>
);
}
}
export default Map;
But how can I send it to Parent
(two levels up) component? In this way, Child
component will only forward the props to the Parent
component, which it took from its child ( ChildofChild
).
It's no problem to just forward props. This includes handlers:
class Parent extends Component {
handle = event => { /* handle event */ };
render() {
return (
<Child handler={this.handle} />
);
}
}
const Child = ({handler}) => (
<ChildOfChild handler={handler} />
);
const ChildOfChild = ({handler}) => (
<button onClick={handler}>Click me!</button>
);
Your top Parent component is not AirbnbMap, is class Map. Also you are declaring handleAdd in your Map class, but your are not passing it to Airbnb component. On Map you should pass it on :
<AirbnbMap handleAdd={this.handleAdd}
// Rest of props here
/>
Then on AirbnbMap class:
<PlaceMarker
...
onAdd={props.handleAdd}
/>
The rest looks ok.
From React 16.3 on, you could use the new Context API so you don't need to pass down the props to every children and be able to lift up state easily and cleanly.
import React, { Component, createContext } from 'react';
const ParentContext = createContext({});
class Parent extends Component {
handle = event => { console.log('Event', event) };
render() {
return (
<ParentContext.Provider value={{
onClick: this.handle,
}}>
<Child />
</ParentContext.Provider>
);
}
}
const Child = () => (<ChildOfChild />);
const ChildOfChild = () => (
<ParentContext.Consumer>
{ (context) => <button onClick={context.onClick}>Click me!</button>}
</ParentContext.Consumer>
);
export default Parent;
The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.