Dynamically passing data to a child component in react - reactjs

I have a react component that is responsible for listing out data and if the user clicks on a particular data item, it renders a popup with the data the user clicked passed to it. The popup is defined in a separate component but a single instance of the popup is defined in listing component as follows:
render(){
return(
...
{tasks.map((task, index) => {
return (
<p><a onClick={() => self.edit(task.id)}>{task.name}</a></p>
);
})}
<EditTaskPopup show={self.state.showEditPopup} onClose={self.onClosePopup} task={self.state.editData} />
...
)
}
The edit function, packages up the data and sets the component's state so that the data in included in the editData variable and the popup is shown as follows:
self.setState({showEditPopup: true, editData: tasks[x]});
This all works fine but my question is how I should correctly receive that data in the popup container, EditTaskPopup. The constructor of EditTaskPopup fire off when the parent component loads, so no user interaction has occurred, so no value is passed in. Same holds true for componentDidMount. I can see the correct value being passed in when the componentDidUpdate fires off, but that also fires off during the normal operation of the popup where I'm collecting information about what the user is typing within the popup and placing those values in state. Is there an event in a component that only fires off when a parent component changes the parameters passed into it but doesn't fire off when state changes within the component itself?

Try utilising lifecycle method: https://reactjs.org/docs/react-component.html#componentdidupdate

Related

FullCalendar: Render dayHeaderContent prop everytime an Event Changes

I use the dayHeaderContent prop from Full Calendar V5 to Inject certain extra content, Whenever I change or update an event and whenever the calendar re renders, the dayHeaderContent prop is being called and my new content is displayed on the top, It all works fine.
The issue comes when I update the event using the eventDrop and eventResize, The event card itself updates fine, but the dayHeaderContent on the top does not re render, until I manually re fetch the events or click the next page and come back to current page.
Is there a way I can make the dayHeaderContent render eveytime eventDrop and eventResize is used? I know I could just use the calendar refresh or refetch event source, But I cannot do that because I am supplying just one single event source static array of events, So I cannot use the refresh methods.
// Here's how I am supplying my events, here data is a static array
calendarRef.current.getApi().addEventSource(data)
// I want the below Inject function to run eveytime an event is update via eventDrop and eventResize props
const injectDayHeaderContent = (args) => {
if (args.view.type === 'timeGridWeek') {
return (
<span>
{convertDecimalToHHMM(showHours)}
</span>
)
}
}
return(
<FullCalendar
.....
.....
dayHeaderContent={injectDayHeaderContent}
/>
)

How to reload a specific tag in reactJS

I want to reload the UserPlaylist tag after running the onChangeTracks() function in order to update it's contents but I'm not sure how to re-execute a specific tag if possible.
Parent Component:
render(){
return(
<li><UserPlaylist onChange={this.onChangeTracks}/></li>
);
}
UserPlaylist Component (Child Component):
render() {
return(
window.addEventListener('DOMContentLoaded', (event) => {
this.getPlaylists() //Have tracks load immediately
}),
<select value={"DEFAULT"} onChange={this.props.onChange}>
<option value="DEFAULT"> Add to Playlist </option>
<option value="new"> New Playlist </option>
{
this.state.users_playlists.map((playlist, index) => (
<option key={index} value={playlist.id}> { playlist.name }</option>
))
}
</select>
);
}
Components will rerender every time their state is updated. So you should update the state of your Parent component whenever the this.onChangeTracks function is invoked. Since this.state.currentTrack is being passed in as a prop to UserPlaylist, once it is updated via a call to setState the UserPlaylist component will receive new props and should rerender those new props accordingly.
Example:
onChangeTracks(val) {
...
this.setState({currentTrack: val});
...
}
EDIT
Here's a Codesandbox that updates the "New Playlist" select element whenever the onChange event is called.
Here's how it works: First, the useEffect hook fires and loads in data from the examplePlaylists variable. In your app, you should populate this with the data you have saved in your database or localStorage, etc. This data is passed to the playlists state which uses the useState hook. Whenever a person selects an option from the dropdown menu, the onChange event is fired and calls the handleUpdatePlaylists function. This function first prompts the user to enter the name of their new playlist. It then updates the playlists state through the updatePlaylistsfunction. Now, since the state has changed, the component will rerender and map over all the playlists, displaying their names in the dropdown menu. All without needing to refresh the page.
Of course, since I don't have a DB to save these to, the new playlists will disappear on page refresh. But you can write logic to save them however you wish.
And while this works, I think that you should look to using buttons and the onClick event instead of a select element to handle this logic. Since a new playlist is created whenever an option is selected, then clicking on the name of any playlist will also create them. This doesn't appear to be intended functionality.
So to summarize: handle updates to your components by changing their state. Once state changes, the components will rerender with their new state available for you to work with.

React: Append component animations on local and redux state updates

I am building a component that renders an animation on every local and redux state update. Local state is updated on a button click and it also makes an ajax request to a remote server to update that an user clicked the button. I update local state on every click like so:
const [selfAnimations, setAnimations] = useState<AnimationRenders>(initialAnimationState);
const onButtonClickCallback = () => {
makeRemoteAjaxCall();
selfAnimations.localEventsCount += 1;
selfAnimations.animationsToRender.push(Date.now);
setAnimations(selfAnimations);
};
I update the data coming in from remote server like this:
useEffect(() => {
if (animationDataCount && animationDataCount > 0) {
const {
updatedLocalEventsCount,
updatedAnimations,
} = filterDedupedLocalEvents(selfAnimations, animationDataCount);
setAnimations({
localEventsCount: updatedLocalEventsCount,
animationsToRender: updatedAnimations,
});
}
}, [animationDataCount]);
I render a div with a component I created:
<div classNames={...}>
<AnimationContainer numberOfAnimations={selfAnimations.animationsToRender.length} />
</div>
<div className={...}>
<MyButton ... />
</div>
On client side, data is received on regular intervals. My AnimationContainer component can render any number of received animations. However, everytime a user clicks the button or client recieves remote data it updates the state and drops previous animations. Looking online I could delay the rerender with css but the behavior I need is to append to existing animations on state updates. Is there a mechanism that I could use for that behavior?
I misunderstood how react renders. React does not necessarily create a new child component and unmount the old component on props/state updates. In my case, instead of storing counts of what needed to be rendered, I need to store distinct keys for what needed to be rendered, because even if the AnimationContainer component receives updates from props, if it only received counts, then it may not trigger rerenders because the count can be the same. I got around this by implementing the props of localEvents as an array of timestamps: number[]. By passing down timestamps to AnimationContainer and refactoring AnimationContainer to check props update and store into state of animations in middle of rendering and a queue of animations to render, I was able to create a component that can receive and continually renders animations based on events.

React pass an event into child component

I'm trying to find a way to pass event to child component in react.
I have something like that:
class ParentComponent extends Component<IProps, IState> {
render() {
return (
<div className={"ParentComponent"} onClick={this.onPageClick}>
... some navbar ...
... some `tabs` component
...
<ChildComponent/>
<AnotherChildComponent/>
...
... some footer ...
</div>
)
}
}
the child components are actually sub pages (changed using the tabs) with lot of logic and data inside them. (so I prefer manage them in separate components rather then one giant page).
some of the inner component have Editable labels which changed into an input (or in other case to a textarea or to MD editor) when the label is clicked.
there is an inner state in the child components when the user enter
into "Edit Mode" of the label. every component can have several of
this editable-labels.
The product request is when the user is clicking anywhere in the page the labels should exit from edit mode, so I need to capture the onClick on the master div like in the example and pass somehow the event into a function into the active child component so it will update it's inner state to exit edit-mode (if any).
Now, the solution I thought is to create a state variable in the parent which will be changed by the onPageClick function and pass into the child components
so they could update the local state. and then reset it on the parent again.
something like:
onPageClick() {
this.setState({ pageClicked: true }, () => {
this.setState({ pageClicked: false }
});
}
...
<ChildComponent pageClicked={this.state.pageClicked}/>
But it will change the parent state twice per click (and thus also the child state) even if not neccesary. the ideal why if I'll find a way to pass some event delegate to the children so a function will be triggered only inside the child when the parent onClick is triggered without any state changes in the parent.
Doe's it possible? do anyone have an idea how to implement something like that?
You are making your problem way more complicated than it needs to be.
You shouldn't listen for clicks on the outside component.
Instead, you should use your text input's onBlur event.
onBlur event is fired whenever a text input loses focus.

How can I make sure a React parent component loads data before its child component mounts?

I'm writing a simple calendar application that uses a common layout to wrap different views of events (month view shows a larger calendar with all the days of the month and events for each day, week view just shows a vertical list of events for that week, etc.). The common layout includes a calendar picker control for selecting the date, and then a list of event categories that can be checked or unchecked to show events relating to sports, entertainment, etc.
When the layout mounts, I'm calling an async Redux action creator to get the list of event categories from the database. When those are retrieved, they're saved in a Redux store with a property of Selected set to true, since they're all selected at initial load.
async componentWillMount() {
await this.props.getEventTypes();
}
When the month view, which is a child of the layout view, mounts, it's grabbing all the events for the given month. Part of the selection process of getting those events is sending the list of selected event categories to the backend so it only gets events from the selected categories.
async componentWillMount() {
await this.props.getWeeks();
}
The problem is, the selected categories list is always empty when the month view goes to grab the events for the month. So it's not going to select anything since no categories are selected.
It seems the only way this can be happening is if the child component is mounting first, or if the parent component is taking so long to get the event categories that the getWeeks process finishes first (this is unlikely as the process to grab the weeks and days and their events is much more involved than just selecting the event category list).
So, how can I make sure the parent component grabs the event categories from the database and puts them in the Redux store before the child component selects its events?
I know one way, probably the best way, to do this would be to have the list of event categories render into the page on the server side, so it's just already present at initial load. I'll probably end up doing it that way, but I'd also like to know how to do it all through client-side actions, in case I need to do it that way for some reason in the future.
You can try like this
Set isDataLoaded when data is available.
Use ternary operator for conditional rendering.
In you render
return(
<>
....
{ isDataLoaded ? <ChildComponent /> : null }
....other sutff
</>
);
Use can also use the && operator
return(
<>
....
{ isDataLoaded && <ChildComponent /> }
....other sutff
</>
);
You can integrate componentDidUpdate() and use it to render your child-components in a somewhat synchronous flow.
Let's say the structure of your Parent Component should look something like the following:
Parent
class Parent extends React.Component{
state = {
renderChildren: false
}
async componentDidMount() {
await this.props.getEventTypes();
}
componentDidUpdate(prevProps){
if(this.props.yourUpdatedReducer !== prevProps.yourUpdatedReducer){
this.setState({
renderChildren: true
})
}
}
render(){
const { renderChildren } = this.state
return(
{ renderChildren ? <Child/> : "Loading" }
)
}
}
You want a key in your state that determines whether you should
render the Child component.
In componentDidMount(), you call the action-creator function, when
it completes, you get updated props.
With updated props, you trigger componentDidUpdate(), where you
check the values in your reducer. If the values are
different that means you got the updated data from your database, so
everything has loaded.
Great, so now you want to mount your Child component, so you
update-state, setting renderChildren to true, thus re-rendering the
Parent component. Now Child gets rendered and should behave as expected.

Resources