M.Morris
M.Morris

Reputation: 147

Update child's props from parent node

I'am trying to realize a filter for a collection of components (like DropDown menu) that have different boolean props.

Initially all my component are rendered but, when I do my choice on filter, I'd like to render only components that have a correlation with choice made. For do that I've think that I should to have an array filled by my components and a container that it will be used with this method ReactDOM(array,container) only when component did mount.

The question is: If I have a collection of components already rendered, all of them child of the same parent, is there a way for update dynamically them's props from parent node? I'd like to do that for implement a conditionally rendering to the purpose of decide which component render.

 class EventsPlanned extends Component{ //the parent
    constructor(props){
    super(props);
    this.state={
        showAllEvents: true,
        showUnmappedEvents: false,
    }
}

componentDidMount(){
    var container = document.getElementById("eventToShowContainer");
    var arr=[];
    var eventsArrayJSON = localStorage.getItem("eventsArray");
    var eventsArray = JSON.parse(eventsArrayJSON);

    if(eventsArray.length !==0){
        for(let i=0; i<eventsArray.length; i++){
            arr.push(<Event key={i}
                        eventName={eventsArray[i].name} 
                        eventPhoto={eventsArray[i].photo} 
                        eventPeriods={eventsArray[i].periods} 
                        eventDescription={eventsArray[i].description}
                        eventTag={eventsArray[i].tag}
                        eventIsDraft={this.state.showAllEvents}
                        eventIsMapped={this.state.showUnmappedEvents}/>);
        }
        ReactDOM.render(arr, container);
    } else {
        ReactDOM.render(this.nothingToShow(), container);
    }
}

filterHandler = (item) =>{  //callback function for dropdown child
    switch(item){
        case "Events Planned":
            this.setState({
                showAllEvents: true,
                showUnmappedEvents: false,
            });
            break;
        case "Unmapped Events":
            this.setState({
                showAllEvents: false,
                showUnmappedEvents: true,
            });
            break;

nothingToShow = () =>{ //in case there aren't event
    return <div className="w3-container">
                <div className="w3-panel">
                    <h1>Nessun Evento da mostrare</h1>
                </div>
            </div>
}

render(){
    return(
            <div>
                <EventsHeader filterHandler={this.filterHandler}/>
                <div id="eventToShowContainer"/>
            </div>
        );
    }
}

export default EventsPlanned;


class Event extends Component{ // the child
    state={
        name: this.props.eventName,
        photo: this.props.eventPhoto,
        periods: this.props.eventPeriods,
        description: this.props.eventDescription,
        tag: this.props.eventTag,
        isDraft: this.props.eventIsDraft,
        showUnmapped: this.props.showUnmapped
    }

    render(){
        if(this.state.showUnmapped){
            return(
                <div className="w3-container">
                    <div className="eventPanel w3-panel">
                        <h1>name: {this.state.name}</h1>
                        <h6>description: {this.state.description}</h6>
                        <h6>{this.state.periods[0]}</h6>
                        <h6>{this.state.tag[0]}</h6>
                    </div>
                </div>
            );
        }
    }

    export default Event;

Upvotes: 0

Views: 83

Answers (1)

amankkg
amankkg

Reputation: 5061

To do so, first, you need to render all children in parent's render method.

Remove any render logic from componentDidMount and render all of the markup in render method directly:

// EventsPlanned component
state = {
  events: [],
  showEvents: false,
  showUnmappedEvents: false,
}

componentDidMount() {
  const eventsArrayJSON = localStorage.getItem("eventsArray");
  const events = JSON.parse(eventsArrayJSON);
  if (events.length > 0) this.setState({ events })
}

filterHandler = filter => ...

nothingToShow() {
  return (
    <div className="w3-container">
      <div className="w3-panel">
        <h1>Nessun Evento da mostrare</h1>
      </div>
    </div>
  )
}

render() {
  const { events, showAllEvents, showUnmappedEvents } = this.state
  return (
    <div>
      <EventsHeader filterHandler={this.filterHandler}/>
      <div id="eventToShowContainer">
        {events.map(e => (
          <Event
            key={e.name}
            eventName={e.name} 
            eventPhoto={e.photo} 
            eventPeriods={e.periods} 
            eventDescription={e.description}
            eventTag={e.tag}
            eventIsDraft={showAllEvents}
            eventIsMapped={showUnmappedEvents}
          />
        ))}
        {events.length === 0 && this.nothingToShow()}
      </div>
    </div>
  )
}

This way every time your parent state changes the children will get updated eventIsDraft and eventIsMapped props, because every state change implies re-render.


The way you're doing it now:

initial parent's render to render container div -> componentDidMount -> getElementById to find container -> ReactDOM.render into that container`

-- is obsolete with React, now you need to forget everything related to imperative DOM changes (hello, jQuery, and bye!), leave it all in 2014, and dive into a wonderful world of explicit state management and declarative UIs.

My proposed solution logic is next:

initial render will render nothingToShow -> componentDidMount will setState if there are any events in localStorage -> setState will trigger render -> now, if events are in state, render will draw our array of Event components

And, on event of filter change:

filterHandler calls setState -> setState triggers render -> render reads updated this.state and renders Event's with updated EventIsDraft and EventIsMapped props

Since now Event component receives actual props from parent all the time, we don't need it to have local state, so it becomes as simple as:

const Event = props => props.showUnmapped && (
  <div className="w3-container">
    <div className="eventPanel w3-panel">
      <h1>name: {props.name}</h1>
      <h6>description: {props.description}</h6>
      <h6>{props.periods[0]}</h6>
      <h6>{props.tag[0]}</h6>
    </div>
  </div>
)

PS: not sure how are you separating draft/mapped events, since your code didn't reflect it clearly

Upvotes: 1

Related Questions