Ant Design: How can I handle Button in Card extra onClick event? - reactjs

I want to click X Button in Card extra to visible "Confirm Remove Todo modal".
UI:
But...
the reality when I click X Button then it visible "Edit Todo modal" from Card event instead.
how can I fix it?
Code:
{todos.map(todo => (
<Card
className={styles.CardTodo}
headStyle={{ textAlign: 'left' }}
bodyStyle={{ textAlign: 'left' }}
key={todo._id}
title={todo.title}
onClick={() => handleSelectTodo(todo._id)}
extra={
<Button
type="danger"
shape="circle"
style={{ color: 'white', zIndex: 10 }}
onClick={() => handleRemoveTodo(todo._id)}
>
X
</Button>
}
>
{todo.description}
</Card>
))}
.
.
Thanks very much, guys
e.stopPropagation() is useful for me.
And then I found another problem.
It is handleRemoveTodo() is the function that opens another modal.
But that modal didn't get "Todo object"
when I remove e.stopPropagation(), the modal will get Todo Object again
Code:
Todo component
const handleRemoveTodo = () => {
setModalConfirmRemoveVisible(true)
}
const handleConfirmRemove = async todoId => {
console.log('Hello', todoId)
setIsRemoveLoading(true)
try {
await axios.delete(`/todos/${todoId}`, apiConfig)
} catch (err) {
console.error(err)
console.error(err.response.data)
}
await fetchTodos()
setModalConfirmRemoveVisible(false)
setIsRemoveLoading(false)
}
return (
{modalConfirmRemoveVisible && (
<ModalConfirmRemoveTodo
visible={modalConfirmRemoveVisible}
todo={todo}
isRemoveLoading={isRemoveLoading}
onConfirmRemoveTodo={handleConfirmRemove}
onCancel={() => setModalConfirmRemoveVisible(false)}
onConfirmRemove={handleConfirmRemove}
/>
)}
)
Modal component
const ModalConfirmRemoveTodo = props => {
const { visible, isRemoveLoading, onCancel, todo, onConfirmRemove } = props
console.log('ModalConfirmRemoveTodo', todo)
return (
<>
<Modal
visible={visible}
title={<Title level={3}>Remove Todo</Title>}
okButtonProps={{ loading: isRemoveLoading, disabled: isRemoveLoading }}
okText="Remove"
okType="danger"
onOk={() => onConfirmRemove(todo._id)}
onCancel={onCancel}
>
Want delete {todo.title} ?
</Modal>
</>
)
}

This is called Event Bubbling. When an event happens on an element, it first runs the handlers on it, then on its parent, then all the way up on other ancestors.
Please refer to this article for details: https://javascript.info/bubbling-and-capturing#bubbling
Below is my solution to your problem. Instead of opening a modal, I just use a simple alert to simulate it.
Your current problem: https://codesandbox.io/s/event-bubbling-bojvq
You will see that the Chrome alert will pop up twice. The former is from the onClick of extra, the latter is from onClick of Card.
Solution: https://codesandbox.io/s/prevent-bubbling-zkxk6
Just add a simple e.stopPropagation() to prevent the bubbling inside extra Button onClick. Please refer to this: https://javascript.info/bubbling-and-capturing#stopping-bubbling for more information.
Back to your code, just simply update your Button's onClick like this:
onClick={e => { e.stopPropagation(); handleRemoveTodo(todo._id)}}

Use stopPropagation() method on your event:
<Button
type="danger"
shape="circle"
style={{ color: 'white', zIndex: 10 }}
onClick={e => { e.stopPropagation(); handleRemoveTodo(todo._id)}}
>
X
</Button>

Related

How to pass OnClick function to React antd Icon React Js

To expand the visual, I need to pass the Onclick function to antd Expand icon(shown below).
const showModal = () => {
setIsModalOpen(true);
};
const handleOk = () => {
setIsModalOpen(false);
};
const handleCancel = () => {
setIsModalOpen(false);
};
This is how i passed it to normal button currently.
<div className="expandButton">
<button
type="button"
className="fullScreenButton"
onClick={showModal}
>
Click
</button>
</div>
Instead of this way can i pass the onclick function directly to react antd icon?
Current antd icon.
<div>
<ExpandAltOutlined style={{ fontSize: "150%" }} />
</div>
Simply add an onClick event
<ExpandAltOutlined style={{ fontSize: "150%" }} onClick={()=>console.log('hj')} />

react popconfirm inside popconfirm

How can I show another Popconfirm inside another Popconfirm?
The function handleDelete it return true or false. I can see on console the log message but i cannot see the second popconfirm.
render: (text, record) =>
<Popconfirm title="Sure to delete?" onConfirm={() => {
if(this.handleDelete(id)){
console.log("it show true")
return (
<Popconfirm title="Force Delete?" onConfirm={() => {
this.handleInside()
}}>
</Popconfirm>
)
}
}}>
<Button type="link"><b>Delete</b></Button>
</Popconfirm>
}
onConfirm appears to be a event handler when you confirm the action on your Popconfirm, so it doesn't make sense to return JSX inside that method and assume that it will somehow magically render it on the screen.
What you can do is setting the state of the force delete status and render it based on that state.
Something like this:
const [forceDelete, setForceDelete] = useState(false);
...
<Popconfirm title="Sure to delete?" onConfirm={() => {
if (this.handleDelete(id)){
setForceDelete(true);
....
{forceDelete && <Popconfirm ... onConfirm={() => { ... setForceDelete(false); }} />}
...

Toggle in using react-strap is opening all toggles at the same time

My toggle is opening all wraps in the same time when i click in one specific, what's wrong?
const SomeData = ({ data, dayNumber }, props) => {
const Exams = () => {
const listExams = data.map((item) => (
<Fragment>
<Wrap key={item.id}>
<WrapCard userID={item.userID}>
<Button color="light" size="lg" block onClick={toggle} style={{ marginBottom: "1rem" }</Button>
<Collapse isOpen={isOpen}>
<Card></Card>
</Collapse>
</WrapCard>
</Wrap>
</Fragment>
));
return listExams;
};
const [isOpen, setIsOpen] = useState(false);
const toggle = () => setIsOpen(!isOpen);
};
Check out your rendered HTML. You'll see this code block for every item in data:
<Fragment>
<Wrap key="someID">
<WrapCard userID="someotherID">
<Button color="light" size="lg" block onClick={toggle} style={{ marginBottom: "1rem" }</Button>
<Collapse isOpen=!!---SAME VALUE HERE---!!
<Card></Card>
</Collapse>
</WrapCard>
</Wrap>
</Fragment>
(Some of the text above in your HTML will look different than what I typed, I only resolved some of it)
The problem is the ---SAME VALUE HERE--- I put in there. They all reference the same isOpen. So you need to have many values in your state, one for each item in data. I'm assuming they have a unique ID. so you could use a state that is an empty object, and when you toggle it on: setIsOpen({...isOpen, item.id: true}) and when you toggle it closed setIsOpen({...isOpen, item.id: false}). Then check for each item in data if it should be open. It may look like <Collapse isOpen={isOpen[item.id]}> which works only if you want it closed as the default state (if the item is undefined) as both undefined and false are falsy.
This would not work if you wanted the default state to be open. Then you'd have to <Collapse isOpen={typeof isOpen[item.id] === 'undefined' || isOpen[item.id] === true}>. There would also be more elegant optional chaining answers.
I suggest you to use Collapsible
Use Collapsible
npm install react-collapsible --save
yarn add react-collapsible
After Installing this modify your code like:
import Collapsible from 'react-collapsible';
const SomeData = ({ data, dayNumber }, props) => {
const Exams = () => {
const listExams = data.map((item) => (
<Fragment>
<Wrap key={item.id}>
<WrapCard userID={item.userID}>
<Collapsible trigger = {<span><Button color="light" size="lg" block onClick={toggle} style={{ marginBottom: "1rem" }</Button></span>}>
<Card></Card>
</Collapsible>
</WrapCard>
</Wrap>
</Fragment>
));
return listExams;
};
};

onBlur for div element in React

I want to perform a function after a div element has left focus.
I'm using tabIndex and onBlur function inside the div. And its working fine when i manually put focus by clicking on any of the elements inside the div. But by default when no item is clicked inside the div, its not working.
My component is a Functional Component & the div is rendered dynamically so also I'm unable to set focus using useRef.
const renderHeaderCell = (header, headerKey) => {
return (
<div className="DataTable__header-cell-wrapper">
{filterable ? (
<IconButton onClick={() => toggleFilterPanel(headerKey)}>
<i className="material-icons">filter_list</i>
</IconButton>
) : null}
{activeFilterHeader === headerKey ? (
<div
tabIndex={0}
onFocus={e => {
console.log("DIV", "focus");
}}
onBlur={e => {
console.log("DIV", "blur");
}}
style={{ border: "1px solid blue" }}
>
DIV container
<input
type="text"
onFocus={e => {
console.log("input", "focus");
}}
onBlur={e => {
e.stopPropagation();
console.log("input", "blur");
}}
placeholder="Inside Textbox"
/>
Click outside
</div>
) : null}
{sortedByColumn === headerKey ? renderSortIcon() : null}
</div>
);
};
Code after i click the icon to show the DIV
const toggleFilterPanel = headerKey => {
if (activeFilterHeader === headerKey) {
setActiveFilterHeader("");
} else {
setActiveFilterHeader(headerKey);
setUniqueItemsForFilter(getUniqueItemsForFilter(rows, headerKey));
}
};
Code after onBlur is called
const onBlur = () => {
console.log("Blured");
};
So how shall i make onBlur to work on a div element?
Following image shows current focus
blur event may be very trick especially if you have focusable elements inside focusable element.
Open console and play with that piece of code a little bit in order to understand better how 'blur' and 'focus' events work.
class BlurExample extends React.Component {
render() {
const {title} = this.props;
return (
<div
tabIndex={ 0 }
onFocus={ () => {console.log('main', 'focus');} }
onBlur={ () => {console.log('main', 'blur');} }
style={ { border: '1px solid coral', padding: '10px', fontFamily: 'sans-serif' } }
>
Click here 1
<input
type="text"
onFocus={ () => {console.log('input', 'focus');} }
onBlur={ () => {console.log('input', 'blur');} }
placeholder="Click here 2"
style={ { margin: '0 10px', padding: '10px' } }
/>
Click here 3
</div>
);
}
}
ReactDOM.render(
<BlurExample />,
document.body
);
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/15.1.0/react.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/15.1.0/react-dom.min.js"></script>
blur event won't happen if you click inside focused element. Exception if you have another focusable element inside and you click on it. But Notice that after main blur you will see input focus and main focus and if you will click outside of the main element you will also see two blur events: input blur and after that main blur

React unmount a Paypal button

I have a Paypal button rendered with a Modal component. What is the proper way to unmount the Paypal Button without raising a clean up error?
Here is the implementation for the Dialog
<Drawer anchor="bottom" open={open} onClose={() => setStatus(false)}>
<section className={classes.innerDrawer}>
<h2 className={classes.innerDrawerTitle}>
{loading ? '' : 'Checkout'}
</h2>
<PaypalButton
...props
/>
</section>
</Drawer>
And the button
const Button = paypal.Button.driver('react', { React, ReactDOM });
return (
<Button
env={PAYPAL_ENV}
client={client}
payment={(data, actions) => payment(data, actions)}
onAuthorize={data => execute(data.payerID, data.paymentID)}
style={{
size: 'medium', // tiny, small, medium
color: 'blue', // orange, blue, silver
shape: 'rect', // pill, rect
}}
/>
);
The error message I get:
Uncaught Error: No response from window - cleaned up
I do not get this error message when the un-mount is successful, which happens when I processed with a payment.
link:
https://codesandbox.io/s/r4zvkjm2kq
I couldn't reproduce your issue, but I've tried to do the same code you're doing.
In this example, the PayPal button is mounted in the Drawer element, which is mounted after a button click. The Drawer is unmounted when you click in any place outside the drawer.
class Modal extends React.Component {
constructor() {
super()
this.state = {
open: false
}
}
render () {
return (
<div>
<button onClick={() => this.setState({ open: true })}>Open Drawer</button>
{
this.state.open &&
<Drawer anchor="left" open={true} onClose={() => this.setState({ open: false })}>
<PayPalButton
commit={this.state.commit}
env={this.state.env}
client={this.state.client}
payment={(data, actions) => this.payment(data, actions) }
onAuthorize={(data, actions) => this.onAuthorize(data, actions)}
/>
</Drawer>
}
</div>
)
}
}
Working demo: https://codepen.io/herodrigues/pen/gqQEgr

Resources