React confirm alert handle key press = enter - reactjs

import { confirmAlert } from "react-confirm-alert"; // Import
import "react-confirm-alert/src/react-confirm-alert.css"; // Import css
export default function App() {
const handleButtonPress = (event) => {
if (event.key === "Enter") {
alert("Click Yes");
}
};
const handleClick = (e) => {
confirmAlert({
title: "Confirm to submit",
message: "Are you sure to do this.",
buttons: [
{
label: "Yes",
onClick: () => {
alert("Click Yes");
},
onKeyPress: () => {
handleButtonPress();
}
},
{
label: "No",
onClick: () => {
alert("Click No");
}
}
]
});
};
return (
<div className="App">
<button
onClick={() => {
handleClick();
}}
>
Click
</button>
</div>
);
}
I'm testing react-confirm-alert.
I'm trying to handle Button Yes by pressing Enter. Both function onClick() for yes and no are working good, but press enter is not working.
Can someone let me know if I did something wrong?

There's several issues. This react-confirm-alert library looks like a poor library, I'd pick a different one if you can find one that suits your needs.
You're calling handleButtonPress() with no arguments, and then you're trying to read from the event object, which you don't pass.
const handleButtonPress = (event) => {
if (event.key === "Enter") {
The event.key line should be throwing an error since event is undefined. Since you're not seeing errors, it's clear this line isn't getting called. You should be using console.log or the debugger to double check what code is getting called.
You should also get in the habit of reading documentation. In this case, the documentation shows that onKeyPress is a top level setting, while you've incorrectly put it in buttons.
Either way, react-confirm-alert doesn't pass the event to the onKeyPress callback: https://github.com/GA-MO/react-confirm-alert/#options so it doesn't seem like this API should exist. It doesn't have any use.
I would either pick a different library, otherwise you'll need to add your own keypress listener to the document, and manually handle the enter key there.

Related

I can't set focus on the input

I have this component https://stackblitz.com/edit/react-ts-u7rw2w?file=App.tsx
When I click and call toggleInputCardName, I see in the console "NO inputRef.current null false". At the same time, an input is rendered on the page and above it I see "null inputRef true".
I tried using useEffect, but the result is the same.
How to understand this? IsInputCardName is simultaneously true and false?
How to set focus on the input in toggleInputCardName?
Try useCallback
const inputRef = useCallback(node => {
if (node) {
node.focus();
node.setSelectionRange(node.value.length, node.value.length);
}
}, []);
It is because the setState is async.
I think about 2 possibilities.
First one :
const toggleInputCardName = () => {
setInputCardNameVisibity(!isInputCardName);
};
React.useEffect(() => {
if (inputRef.current) {
console.log('YES inputRef.current', inputRef.current);
inputRef.current.focus();
inputRef.current.select();
} else {
console.log('NO inputRef.current', inputRef.current, isInputCardName);
}
}, [isInputCardName]);
Second one, you could simply add autofocus on the input and don't use ref :
<input
className="input-card-title"
type="text"
value="value"
autoFocus
/>
You need useLayoutEffect:
const toggleInputCardName = () => {
setInputCardNameVisibity(!isInputCardName)
}
React.useLayoutEffect(() => {
if (!inputRef.current) return
inputRef.current.focus()
inputRef.current.select()
}, [isInputCardName])
The reason it doesn't work in the handler or in a plain useEffect is that they are executing before the input is actually written to the DOM. State changes in react are flushed later, and don't happen immediately. useLayoutEffect waits until the DOM change is committed.
Another way of doing it is by wrapping it in a 0 second timeout. This looks hackier than it is, as a timer with 0 as the time to wait, will just wait until the current call stack is finished before executing.
const toggleInputCardName = () => {
setInputCardNameVisibity(!isInputCardName)
setTimeout(() => {
if (inputRef.current) {
console.log('YES inputRef.current', inputRef.current)
inputRef.current.focus()
inputRef.current.select()
} else {
console.log('NO inputRef.current', inputRef.current, isInputCardName)
}
}, 0)
}
But I'd probably useLayoutEffect.
#OneQ answer of using the focus attribute also makes a lot of sense and reduces noise.

React: Remove focus from input element on escape key

I have an input tag, when I press the escape key, I want the focus on the input element to go away, meaning there is no longer a text cursor or any focus styling(when the user types, their input will not go into the input tag).
The code below is my attempt at implementing the functionality described above, but it does not work as intended. I would like to get this code to work as intended.
import React, { useRef } from 'react';
const onEscape = function (action) {
window && window.addEventListener('keydown', (e) => {
if (e.key === "Escape") {
action();
};
});
};
const MyComponent = () => {
...
const descRef = useRef();
onEscape(() => {
descRef.blur();
});
return (
<div>
<input
ref={descRef}
...
/>
</div>
);
};
I have tested that onEscape function and it works when other actions are passed to it(when the user presses the escape key, it executes whatever function has been passed to the onEscape function)
Just from glancing at your code all you need is to add the current property to the ref
Try this
onEscape(() => {
descRef.current.blur();
});

Smart PayPal Buttons in React, disable the button

I have this implementation of the PayPal smart buttons in React:
function PayPalButtonComponent(props: PayPalButtonProps) {
const [show, set_show] = useState(false);
const [error, set_error] = useState<string>();
const create_order = (_: any, actions: any) => {
return actions.order.create({
purchase_units: [
{
amount: {
currency: props.currency || "EUR",
value: props.total
}
}
]
});
};
const handle_approve = (_: any, actions: any) => {
return actions.order.capture().then((details: any) => {
if (props.onSuccess) props.onSuccess(details);
});
};
const handle_cancel = () => {
if (props.onCancel) props.onCancel();
};
const handle_error = () => {
if (props.onError) props.onError();
};
const render_button = () => {
const Button = paypal.Buttons.driver("react", { React, ReactDOM });
return (
<Button
style={{
layout: "horizontal",
size: "responsive",
shape: "rect",
color: "gold",
tagline: false
}}
funding={{
allowed: [paypal.FUNDING.CARD, paypal.FUNDING.PAYPAL],
disallowed: [paypal.FUNDING.CREDIT]
}}
createOrder={create_order}
onApprove={handle_approve}
onError={handle_error}
onCancel={handle_cancel}
/>
);
};
useEffect(() => {
if (props.isScriptLoaded) {
if (props.isScriptLoadSucceed) set_show(true);
else set_error("Unable to load the paypalscript");
}
}, [props.isScriptLoaded, props.isScriptLoadSucceed]);
if (error) return <p>{error}</p>;
if (!show) return <FakeButton />;
return render_button();
}
I have struggled to implement these buttons in react since there is no documentation, I have found and copied some code from here and trying to guess other stuff. But I can't understand how to disable the button.
In this guide they state that one can call the disable() method on the actions object but can't figure out how I can accomplish that with my configuration.
Have you ever try something similar? Do you know any documentation one can follow?
edit
What I'm trying to accomplish is to set the button in a disable state during the payment. I know there is the paypal overlay, but when the transaction completes I change the app route and since it happen when onSuccess is called, due to the apparent async nature of actions.order.capture() this can't happen instantaneously, and so there is a moment when one can click again on the paypal button. If i can disable the button i have solve the problem.
The onInit implementation allows you to disable/enable the button before clicking on it, useful for some sort of validation before the checkout (like terms checked) but doesn't apply to my case. I have also tried to call actions.disable() in create_order but that breaks the button.
Have same situation here and the onInit example from documentation is not that straight forward as some think... You would need to create hidden imput and assign somsort of variable to it when payment made in order to achieve buttons to be disabled after payment. To bad no one solve this one.

Detecting user leaves page with custom popup in reactjs

I want to open a popup to warn the user that their data would be lost if they leave the form without submitting it.
I've heard about Prompt can do it, but they didn't support I customize the Popup, button, or handling confirmation button or cancel.
is there any way or component that can help me to show the popup prevent the user leaves the form with 2 customize buttons.
Thank you in advance.
use react-confirm-alert
install:
npm i react-confirm-alert
using:
import { confirmAlert } from 'react-confirm-alert';
import 'styles/react-confirm-alert.css';
create this customize function
const MyCustomConfirm = (content, onAccept, title, acceptTxt, rejectTxt) => {
acceptTxt = acceptTxt || "Yes";
rejectTxt = rejectTxt || "No";
title = title || "Message";
confirmAlert({
title: title,
message: <div className="react-confirm-alert-message">{content}</div>,
buttons: [
{
label: acceptTxt,
className: "btn-secondary", // your own css
onClick: () => {
if (onAccept) {
onAccept();
}
}
},
{
label: rejectTxt,
className: "btn-secondary", // your own css
onClick: () => { }
}
]
});
}
now you can call it anywhere. example :
MyCustomConfirm(' Do you want to clear all ? ', function () {
searchInput.val("");
$(this).val("");
});
use onBlur event on your parent element of form
myFunction = () =>{
MyCustomConfirm(' Do you want to clear all ? ', function () {
searchInput.val("");
$(this).val("");
});
return false;
}
render(){
// your parent element of form
<form onBlur={this.myFunction()>
</form>
}
or you can call this popup when user try to close your form.
or even the document level you can use this :
window.addEventListener("beforeunload", function (e) {
if (formSubmitting) {
return undefined;
}

Show custom dialog setRouteLeaveHook or history.listenBefore react-router/redux?

I cant seem to figure out how to show a custom dialogue instead of using the normal window.confirm that routeWillLeave and history.listenBefore uses. Basically i have built a notification system and check if a form is dirty const { dispatch, history, dirty } = this.props;
if the form is dirty it means the user has unsaved changes. If they change route I would like to show my notification which will have two buttons STAY, IGNORE which can both take an onClick handler.
Ive spent a bit of time googling and havent come across any mention of how i might accomplish this using routeWillLeave. The closest thing i could find was to use history.listenBefore however there docs say that I need to do this.
let history = createHistory({
getUserConfirmation(message, callback) {
callback(window.confirm(message)) // The default behavior
}
})
But I am using browserHistory from react-router to initiate my store const history = syncHistoryWithStore(browserHistory, store);
How can I stop a route change after a link has been clicked, show a notification using my custom notification system and depending on which button is clicked either transition to the new route or stay?
Here is an example of how my notification system works and the direction ive headed in which obviously doesn't work because all this returns is a string to show in the window.confirm dialogue by default.
history.listenBefore((location) => {
if(this.props.dirty){
const acceptBtn = {
title: 'STAY',
handler: ignoreRouteChange() //This can be any function I want
}
const denyBtn = {
title: 'IGNORE',
handler: continueRouteChange() //This can be any function I want
}
return dispatch(addNotification({
message: 'You have unsaved changes!',
type: NOTIFICATION_TYPE_WARNING,
duration: 0,
canDismiss: false,
acceptBtn,
denyBtn
}));
return "Usaved changes!"
}
});
Thanks
Another solution that i have decided to use is to return false in the normal setRouterLeaveHook callback and then show my dialog and use the nextLocation passed to the callback to push the next route depending on button selection.
router.setRouteLeaveHook(route, this.routerWillLeave.bind(this));
routerWillLeave(nextLocation){
let { dirty, dispatch, resetForm } = this.props;
if (dirty) {
let dialog = {
id: Date.now(),
showTitle: true,
titleContent: 'Unsaved Changes',
titleIcon: 'fa fa-warning',
content: <span>You have <strong>unsaved</strong> changes! <strong>Discard</strong> them?</span>,
type: 'confirm',
handleCloseClick: (e) => {
e.preventDefault();
dispatch(closeDialog());
},
acceptBtn: {
title: 'Okay',
handler: (e) => {
e.preventDefault();
resetForm();
// Wait for call stack to unwind and then execute
// the following which will now have the updated values.
setTimeout(() => {
dispatch(push(nextLocation));
dispatch(closeDialog());
}, 0);
}
},
denyBtn: {
title: 'Deny',
handler: (e) => {
e.preventDefault();
dispatch(closeDialog());
}
}
}
dispatch(addDialogWindow(dialog));
dispatch(openDialog(false, (e) => dispatch(closeDialog()), false));
return false;
}
return true;
}

Resources