reactjs componentWillMount not calling every time when i open modal - reactjs

i am displaying table data inside modal dialog, when i click table row to edit data it will open another modal dialog, here i am getting row data by using clicked row id inside componentWillMount(). but when i close edit modal dialog and try to edit other table row componentWillMount() will not called. so edit modal dialog is empty. why componentWillMount not calling every time?

As Nir H already mentioned, Dialog component mounts into the dom only once, and then after you close it, becomes invisible (by changing the left position to -10000px) but still present inside the DOM.
So its clearly that componentWillMount and componentDidMount methods will be called only once. If you want to open Dialog component with different options every time, you can do the folloving:
Save ref to the Dialog component instanse in the parent component:
ref={instance => {this.RowDialog = instance}}
Then you can open Dialog by using it handleOpen method:
onRowClick = (event) => {
this.RowDialog.handleOpen();
}

I used following steps to fix it.
i used {(this.state.open) ? <Dialog open={true} ></Dialog> : null
second step by unmounting the component componentWillUnmount() { }

Related

How to call or use a .js file using onclick Reactjs functional component

I have .js file called Edit which is used for edit purpose. I have another .js file called Add. I have a dialog box in edit.js which will open when I click a button. But I don't want to use a button instead I want to get that a dialog box when I click anywhere on box. I tried using onclick in div tag but I didn't get any response.
this is the output
so if you observe we got a edit button there if click it I am getting form/dialog box for editing the content. but I want that form or dialog box to open when I click anywhere on the yellow box.
<div id='color' className='div2' key={item.id} style={{width: 340,
# border: '5px solid white',textIndent:-30,paddingRight:32,paddingLeft:40,whiteSpace:'pre',paddingTop:15, backgroundColor:item.currentColor}} onClick={()=>{editpage(item.id)}} >
this is what I used for calling function for getting form in another .js file. It is part of a mapping function.` there is a onclick event I used that whenever I click on the box or content which is all under div tag I need to go to that function and there go to edit and then form but it didn't work
the function to which it goes is this:
const editpage=(id)=>{ <Edit id={id}></Edit> }
I want to send a id as a parameter which is passed to Edit.js.
I used <Edit/> because it is a another js file which I am importing in Add.js file.
I am not able to get the output can you please me with this.
how I can use this when I click on color box should open a form which is indeed in another file.
This is successfully calling a function on the click event:
onClick={()=>{editpage(item.id)}}
But what does that function do? Absolutely nothing:
const editpage=(id)=>{ <Edit id={id}></Edit> }
The function doesn't return anything (not that returning from a click handler does anything either of course), doesn't perform any operations, nothing. It's just a single JSX expression.
Take a different approach entirely. Add your component to the markup, but wrap it in a condition based on state. For example, consider this state value:
const [openEditPage, setOpenEditPage] = useState();
The purpose of this state value is to keep track of which item.id should currently have its "edit" component visible. (Default to undefined so none are visible by default.)
Use this value when rendering the components:
<div id='color' className='div2' key={item.id} style={/*...*/} onClick={()=>{editpage(item.id)}}>
{openEditPage === item.id ? <Edit id={item.id}></Edit> : null}
</div>
So in this case the <Edit> component will only be rendered if openEditPage === item.id. Which is what the editpage() function can do:
const editpage = id => setOpenEditPage(id);
Clicking on the <div> would invoke the editpage function, which updates the state value, which triggers a re-render, and in the new render the <Edit> component is shown because the condition would be true.

How do I prevent unnecessary, repetitive side-effects from my React useEffect hook?

I am having trouble preventing unnecessary side-effects from a useEffect hook linked to a component that gets re-rendered/reused multiple times after being clicked. I want the side-effect to trigger an action once, but it is being triggered for every render.
I'm building a task viewer that displays nested task data. Please see screenshot #1 for reference.
For context, the main display shows the main tasks (see Event Data) and renders a clickable button if the task has sub-events. When this button is clicked, the selected main task is displayed at the top of the hierarchy view (see Event Hierarchy) and its sub-events are displayed below the hierarchy in a separate pane (see Event Details).
Like the main tasks, if these sub-events in 'Event Details' have their own sub-events, they are also rendered with a clickable button. When this sub-event button is clicked, this clicked sub-event is added to the bottom of the hierarchy, where the clicked main task is already displayed in bold. This selected sub-event's sub-events then replace the content in the 'Event Details' pane.
As the user clicks through the nested data, the clicked sub-event is added to the bottom of the hierarchy so that the user has an idea of where he is in the nested data and its sub-events displayed in 'Event Details'. All 'Event Hierarchy' and 'Event Details' data is cleared when the user selects a new main event or selects a new page.
The hierarchy events are held in an array managed via useState and every time another sub-event is clicked, it is added to this array. That's the idea, at least.
#1
My problem is this:
If I place my setHierarchy function inside a useEffect hook with the selectedTask as dependency, it renders the selectedTask in the hierarchy instantaneously, but the button component that triggers setHierarchy is re-rendered for every sub-event being displayed in 'Event Details' (as I want each event to be clickable) and in doing so, it adds that many copies of the event to my hierarchy array. This happens even though I am checking to see if the hierarchy array already contains the selected subevent before adding it. See result in screenshot #2.
I have tried various configurations of checking the array, but I cannot seem to stop it from adding these copies to and subsequently displaying them in the Hierarchy.
If I place the setHierarchy function inside my click handler, only one single event is added, but it executes before the selectedSubEvent has been updated. This means the hierarchy array is empty upon first render and stays one click 'behind' ie. a specific event is only displayed upon the following click event, after the click that selected it.
#2
This is all done inside my ExpandSubEvents button component (see code below) and also managed via a context provider.
I have tried moving the setHierarchy into a separate function, inside a useCallback, and triggering it from both the clickHandler and the useEffect that sets the selectedSubEvent. This did not resolve the issue.
I've also tried useRef to try and link it to the latest state. I'm not sure that's even doable/correct.
What am I doing wrong here? I am fairly new to coding, so any input on this would be much appreciated.
Sidenote: I suspect that my setup is perhaps beyond the intended scope of useContext. Is it? What can I do to make improvements? Is this perhaps in any way responsible for my issue?
Thank you for taking your time to read this far. I appreciate it!
Deon
ExpandSubEvents Component
import React, { useCallback, useContext, useEffect, useMemo } from 'react';
import SubEventContext from '../../store/sub-event-context';
import classes from './ExpandSubEvents.module.css';
const ExpandSubEvents: React.FC<{
id: number;
subEvents: number;
}> = React.memo((props) => {
// Extract context
const subEventCtx = useContext(SubEventContext);
const {
subEvents,
subEventParentId,
selectedSubEvent,
hierarchy,
setSubEventParentId,
setFetchId,
setSelectedSubEvent,
setHierarchy,
} = subEventCtx;
// Get id of event for when it is clicked
const id = React.useMemo(() => props.id, [props.id]);
let eventIds: number[] = useMemo(() => [], []);
if (hierarchy) {
for (const event of hierarchy) {
eventIds.push(event.id);
}
}
// Set CSS classes to style button if it has sub-events
let subEventQuantity = props.subEvents;
let importedClasses = `${classes['sub-event-button']}`;
if (subEventQuantity === 0) {
importedClasses = `${classes['no-sub-events']}`;
}
// Push the event to the Hierarchy display
// NOTE Tried moving the setHierarchy to a separate function, but it did not make a difference
// const triggerHierarchy = useCallback(() => {
// if (!eventIds.includes(id))
// setHierarchy((prevState) => [...prevState, ...selectedSubEvent]);
// }, [eventIds, id, selectedSubEvent, setHierarchy]);
// Respond to subevent button click event
const clickHandler = useCallback(() => {
setSubEventParentId(id);
setFetchId(id);
// This setHierarchy works, but executes before the selectedSubEVent has been updated
// Furthermore, if a new subevent is selected, it checks if the NEW clicked one has been added
// BUT sends the OLD event still in selectedSubEvent to the hierarchy before IT has been updated
// meaning that the check does not stop the same event being added twice
if (!eventIds.includes(id))
setHierarchy((prevState) => [...prevState, ...selectedSubEvent]);
}, [
eventIds,
id,
selectedSubEvent,
setFetchId,
setHierarchy,
setSubEventParentId,
]);
// NOTE Tried useRef to get setHierarchy to use the latest selectedSubEvent
// const subEventRef = useRef<Event[]>([]);
// subEventRef.current = hierarchy;
// Trying to setHierarchy directly from its own useEffect
// useEffect(() => {
// if (!eventIds.includes(id))
// setHierarchy((prevState) => [...prevState, ...selectedSubEvent]);
// }, [eventIds, hierarchy, id, selectedSubEvent, setHierarchy]);
// Filter the event from the subEvent array and set it to selectedSubEvent
useEffect(() => {
setSelectedSubEvent(
subEvents.filter((subEvent) => subEvent.id === subEventParentId)
);
}, [setSelectedSubEvent, subEventParentId, subEvents]);
return (
<button onClick={clickHandler} className={importedClasses}>
{subEventQuantity}
</button>
);
});
export default ExpandSubEvents;

With ReactJS Popup, how can I make it only show one pop up at a time?

I am experimenting with different React popup libraries. I found reactjs-popup and began playing with the codesandbox. I created a fork of the environment here https://codesandbox.io/s/pp60zjkxlj
When I click the first (or second) button, it displays the corresponding popup. What i'd like is so that when I click other button, it will display that button's popup and hide any other popups that are visible. The goal is that only one popup should be visible on screen at any time.
Is this possible with this library?
See https://codesandbox.io/s/pp60zjkxlj
have a common state for popup when first button is clicked set the state to first button and based on the state
I would look into Portals where you can use a portal that sits outside of the jsx that wishes to use it.
imo it is the best way to implement a modal and i believe there's not to much need to add another dependency to your project and just create one this way that matches your needs.
You will need to pass props into the Popup to tell it when to open and when not to open.
Your component should have state set for each popover or be passed in props to tell the Popovers when to display. You can set in a constructor a default state and spread those before setting the new state with the new Popover open. This is how I handle modals.
constructor(props) {
super(props);
this.state = {
vendor: {} // Just showing my default state this gets set and this.defaultState is spread after my fetch request completes in componentWillMount()
};
this.defaultState = {
isPopoverOpen: {
// ---- Default Popover booleans can go here
editVendor: false
}
};
}
Actual function that controls the Popover status in the state.
// onClick of element would be onClick={this.handleTogglePopover('editVendor')}
handleTogglePopover = id => {
const currentState = Object.assign({}, this.state);
this.setState({
currentState,
...this.defaultState, // spreading default will set everything to false
isPopoverOpen: {
[id]: true // then the newly clicked to true
}
});
};

Dynamically passing data to a child component in react

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

Manually update DraftJs ContentState with clicked text

How can I manually update DraftJs's ContentState in response to clicked text?
I have a list of text item. When one is clicked I am passing that text down to Draftjs, but because I am setting the state using componentWillReceiveProps() it requires that I click the text twice to get an update.
componentWillReceiveProps() {
const activeNoteText = this.props.activeNoteText;
if (activeNoteText !== '') {
this.setState({ editorState: EditorState.createWithContent(ContentState.createFromText(activeNoteText)) });
}
}
First click: Update the App state and pass props down to Draftjs (component updates before receiving new props)
Second click: Now the prop is properly set and Draftjs updates (component updates with the props received on the first click)
How can I accomplish this in one pass? I know there's no componentDidReceiveProps and I know there's a good reason, though I can't claim to fully understand yet, so what's the best practices way to accomplish something like this?
Why are you using componentwillReceiveProps?.
What you can do, is have the states your setting in Draftjs i.e. editorState(Well, that's what I can make out) in its parent and whenever a list item is clicked on the click handler for that update the editorState and then pass it as props to Draft js.
Further for the condition, where you are checking if it is not empty,
You could use the
getInitialState(){
.....
}
For initialization when your component is initially loaded. So you could have a default value for editorState.

Resources