Reputation: 147
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
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
willsetState
if there are any events inlocalStorage
->
setState
will triggerrender
->
now, if events are in state,render
will draw our array ofEvent
components
And, on event of filter change:
filterHandler
callssetState
->
setState
triggersrender
->render
reads updatedthis.state
and rendersEvent
's with updatedEventIsDraft
andEventIsMapped
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