React component is triggering unwanted navigation/uri query - javascript

I have been working with React for YEARS and have yet to encounter this phenomenon. If you can crack it, you'll be my hero forever 😂
The Setup
I have a shared component. It is a wrapper for some buttons that appear at the bottom of several different pages (most use cases are steps in a form. The last of those buttons ALWAYS throws a modal to cancel setup.
I am using the shared component in ALL the following use cases:
✔ The first page of a multi-step form. Works fine, throws modal.
✔ The second page of a multi-step form. Works fine, throws modal.
❌ The third page of a multi-step form. Fails. See description below
❌ The fourth page of a multi-step form. Fails
✔ The review page after the form is completed. Works fine again.
The component itself contains just a button that triggers a useState() setter locally, which toggles the modal open/closed.
The Problem
On the pages where it doesn't work, the component throws all the form values on the page up into the URL as a query string.
see image - green underlined portion is added on modal trigger
This triggers a page reload and of course things break.
Expected Result
I should be able to click "cancel" button and the modal pops. Nothing more, nothing less.
The (relevant) code
const IntakeLossButtons = ({
// PRIMARY BUTTON PROPS
buttonText,
onButtonClick,
buttonDisabled,
buttonSpinning,
buttonAriaLabel,
// CANCEL LINK AT BOTTOM
idToCancel,
saveAction,
onCancel
}) => {
const [showCancelModal, setShowCancelModal] = useState(false)
return (
<div className={styles.buttonsWrapper}>
<SpinnerButton
type="primary"
text={buttonText}
disabled={buttonDisabled}
spinning={buttonSpinning}
onClick={onButtonClick}
ariaLabel={buttonAriaLabel}
/>
<button
className="btnLink"
id="cancel-id"
tabIndex="0"
aria-label={translations.cancelBtnAria ||"Opens a modal to cancel your claim"}
color="secondary"
onClick={()=>setShowCancelModal(true)}
>
Cancel Setup
</button>
<CancelModal
isOpen={showCancelModal}
idToCancel={idToCancel}
onCancel={onCancel}
onSaveForLaterAction={saveAction}
onCloseModal={()=>setShowCancelModal(false)}
/>
</div>
)
}
export default IntakeLossButtons;
WTF?
What would cause this type of behavior? I have tried googling and reading what might cause this. I have logged everything I can log and I still don't know what is causing such a weird behavior.
Any ideas?

It sounds like maybe you forgot to do:
e.preventDefault();
inside the onSubmit handler of your form. In fact, it looks like you don't even have a <form> at all, just a <button> (one with no type attribute, which means it will default to being a submit button).
You need to wrap the button in a <form> with such a submit handler. If you don't do that, when you "submit the form" (ie. click the button) it will use all the form's inputs to generate a new URL, and then it will send you to do that URL.
This is the "default" behavior for forms in browsers. If you go to www.google.com, for instance, and search for "cute puppies", you'll see the URL changes to have ?q=cute+puppies, because Google relies on not preventing this default behavior.

Related

Close a React modal, without the changed values go back to default values?

I have a React modal form for users to input some information.
The form takes in an object as property. When the property is not undefined, the modal shows up, and when it's set to undefined, the modal disappears.
The form have some default values, but when user changed some of the fields, and close the form or save the form (which cause it to close), since there's an closing animation, the fields values go back to default values before it's completely closed.
I can also set an open flag for the modal, but either way, the property has to be set to undefined so a flag does not work neither.
Is there any way to delay the property change, until the modal is closed?
To be more specific:
const SomeComponent = () => {
const {data, setData} = useState(undefined);
return ...
<Modal data={data} open={!!data} />
...
}
This way, when setData({...someobj}), the modal shows up; and setData(undefined), it closes, but before the close animation ends, the default values of the modal form (some maybe blank) replaces the updated values, so the form appears to be in its initial state.
I usually ignore this or by using a second open flag to control the modal opening: <Modal data={data} open={open} />. Though if setData(undefined) the same time as setOpen(false), same thing happens.
It'd be great if I can "freeze" the update of the modal when it's to be closed (before the closing animation ends).
Or maybe just use that open flag, and setDatea(undefined) in a setTimeout?
Is there any standard / recommended way to do this?

How to disable button when clicked button to action window location and enable it again after window location finished [duplicate]

When I am clicking a submit button on my HTML form, the function related to the button is called and it does its work. After completing the work a mail notification is sent which is consuming too much time. After that a confirmation message is displayed on the same HTML page (without using Ajax; i.e., the page is refreshed).
I want to avoid letting the user click the submit button multiple times in confusion during the waiting period of sending mails. So I am thinking that I should disable the button after it is pressed once.
How can I do this?
Can you please suggest any other technique to achive this goal without disabling the button?
Simply:
<form action="file" method="post" onsubmit="this.submit_button.disabled = true;">
<input name="submit_button" type="submit" value="Submit" />
</form>
You can achieve this without disabling the button by using a nonce, however it is a bit more complex. Essentially, when the user requests the page that has the form that will be submitted, assign a unique id to that user's request (store it somewhere on the server, and make sure it's submitted along with the form). When the form is then submitted, look up the unique id to make sure it's not in process or already processed, and then if it's OK to proceed, mark the unique id as "in process", process the form, and then mark it as processed. If when you do the initial check and the page is in process or already processed, you'll need to take the necessary action (redirect them to a confirmation page if it was successfully processed, or back to the form if it was not successfully processed).
How can I do this?
You can take a look at the javascript code in this page:
http://www.codinghorror.com/blog/archives/000096.html
<input type="Button" onClick="this.value='Submitting..';this.disabled=true;" value="Submit">
Can you please suggest any other technique to achive this goal without disabling the button?
Show a busy panel:
"... Your request is being processed please wait..."
(source: vodafone.co.uk)
If you disable a button right before submitting, then the parent form will not be submitted. You need to disable the button after submitting. Best way it to use JavaScript's setTimeout() function for this.
<input type="submit" id="foo" onclick="setTimeout('document.getElementById(\'' + this.id + '\').disabled=true;', 50);">
50ms is affordable enough to give the form the chance to get submitted.
To enhance the user experience more, you could of course append a message or a loading image dynamically during the same onclick event as already suggested by others.
Assuming you don't want to disable the button you could always pop up a modal on the page. This will block the user's interaction with the page. You could throw some kind of loading spinner in there with a message that the submit is in progress.
I don't understand why it is a problem, as you are doing a regular submit, the user should see a white page while you are processing in the back end.. But in case if you want to disable the button, here is the code, use it on the button
onclick="this.disabled=disabled"
You could have the button be disabled, but still seem active to the user. In the function that gets called after the button is hit the first time, have the first thing it does set a global variable like disableButton to true. When the user presses the button, have that go to a function called something like checkSubmitStatus. If disableButton = true, return false. if disableButton = false, trigger the submit function.
You have still disabled the button, but your users can press away unaware.
I'm not submitting anything, but Google Chrome 31 doesn't update the button look while calculating, so i came up with this workaround:
<style>
.btnMenu{width:70px; font-size:12px}
.btnMenu:disabled{background-color:grey}
</style>
<input type="button" class="btnMenu" value="Total" onmousedown="b=this; b.disabled=true; b.v=b.value; b.value='Calculating...'; setTimeout('updateTotals(); b.value=b.v; b.disabled=false', 100)"/>

Nuxt.js: async fetch declares b-modals twice

Maybe I'm not understanding well enough how nuxt works, but apparently my b-modals (bootstrap vue) are being declared twice whenever I use async fetch on a page. I'll explain how my code is structured (can't upload all of the code because of company policies):
I have a default layout in the layouts folder.
<template>
<div>
<wrapper-navbar />
<nuxt class="body" />
<wrapper-footer />
</div>
</template>
Very simple stuff. The wrappers are declared in my components folder. The wrapper-navbar is a normal bootstrap navbar with modifications, and there I declare a b-modal with the unique id #login-modal which gets called whenever a user wants to login pressing a button with the attribute v-b-modal.login-modal. Everything works as expected, the user clicks the login button and a modal appears. The problem comes when in a page (in the pages folder) I call async fetch(). In these cases, when the user presses the login button, the login modal appears twice. I noticed that in these pages the methods created() and mounted() get called twice too, so I think that somehow the modal is getting declared twice.
// Inside pages/index.vue for example
async fetch() {
try {
this.data = await getSomeAsyncData();
} catch (ex) {
console.log(ex);
}
}
This only happens when I have an await inside it. If I only print something or just avoid using fetch, the modal just appears once. What am I doing wrong? How can I avoid this? Thanks for your feedback.

Is there an accessible way of telling that some part of UI can redirect to other page if some asynchronous condition is resolved after clicking link?

Let's say I have the following component
function ClickButton(props) {
const history = useHistory();
const onClick = () => {
history.push('/next-page');
};
return <button onClick={onClick}>Go to next page</button>;
}
More accessible version would be to use Link from react-router as the following
function ClickButton(props) {
return (
<Link to="/next-page">
<span>Go to next page</span>
</Link>
);
}
But what if redirection depends on some http call success, like
function ClickButton(props) {
const history = useHistory();
const onClick = async () => {
try {
await axios.get('https://google.com')
history.push('/next-page');
} catch(err) {
setAlertMessage('Google can't be reached - check your internet connection');
}
};
return <button onClick={onClick}>Go to next page</button>;
}
What is equivalent (and is there some semantically meaningful way) of using Link (or something else that e.g. screen readers would treat as link-alike) here, in case of async handler of onClick event?
I'd probably opt to use the Link component since semantically it makes the most sense to. You can use button element, but then you would need to add all the appropriate accessibility attributes, i.e. role="link" and correct click and keypress event handlers, etc...
You can add an onClick handler to the Link and do the same check. The key is preventing initially the default link behavior.
const ClickButton = props => {
const history = useHistory();
const onClick = async e => {
e.preventDefault();
try {
await axios.get("https://google.com"); // NOTE *
history.push("/next-page");
} catch (err) {
alert("Google can't be reached - check your internet connection");
}
};
return (
<Link to="/next-page" onClick={onClick}>
<span>Go to next page</span>
</Link>
);
};
* NOTE: this GET request seems to just fail in codesandbox.
The simplest way to do this is with a hyperlink. (<link>) as #Drew Reese pointed out.
There is nothing wrong with intercepting the hyperlink action via JavaScript and reporting back if there is an error.
To do this you would just use a standard e.preventDefault(), make your AJAX call and redirect if it works. If it fails then create an alert that explains that the call failed.
Why use an anchor / hyperlink?
Semantics - accessibility is all about expected behaviour. If I change page then a hyperlink makes a lot more sense than a button. It also means I understand that the page will change from the element in case your description does not make sense.
JS Fallback - it could be your SPA does not function at all without JS. However if it does work in some limited fashion without JS then using a hyperlink means that if a user has JS disabled (around 2% of screen reader users don't use JS) or an error causes your JS to fail then navigation is still possible with a hyperlink.
SEO - I dare to swear on Stack Overflow and mention the forbidden term! Although search engines are very good at understanding JS they still aren't perfect. They understand hyperlinks perfectly. If SEO matters use a hyperlink.
Styling - hyperlinks have :visited and :active whereas buttons do not.
How to handle async loading.
When you click on the element you need to let the screen reader know an action is being performed.
The easiest way to do this is to have a visually hidden div located on the page that has the aria-live="polite" attribute. (You can use the same div for all actions).
Then when a function is called that contains an async action you set the contents of that div to "loading" or "waiting" (whatever is appropriate).
This will then get announced in the screen reader (you could argue aria-live="assertive" is better but I will leave that to you).
If the action fails then set your alert message (but make sure that your alert has the appropriate WAI-ARIA roles and behaviour. I would suggest role="alert" but without seeing your application that is a best guess. It could equally be role="alertdialog" if you want a confirmation from the user.
Final thought
When navigation occurs in a AJAX powered SPA, don't forget to set the focus to something on the new page. I normally recommend adding a <h1> with tabindex="-1" (so you can focus it) and setting the focus on that (as your <h1> should obviously explain what the page is about).

Getting Error "Form submission canceled because the form is not connected"

I have an old website with JQuery 1.7 which works correctly till two days ago. Suddenly some of my buttons do not work anymore and, after clicking on them, I get this warning in the console:
Form submission canceled because the form is not connected
The code behind the click is something like this:
this.handleExcelExporter = function(href, cols) {
var form = $('<form method="post"><input type="submit" /><input type="hidden" name="layout" /></form>').attr('action', href);
$('input[name="layout"]', form).val(JSON.stringify(cols));
$('input[type="submit"]', form).click();
}
It seems that Chrome 56 doesn't support this kind of code anymore. Isn't it? If yes my question is:
Why did this happened suddenly? Without any deprecation warning?
What is the workaround for this code?
Is there a way to force chrome (or other browsers) to work like before without changing any code?
P.S.
It doesn't work in the latest firefox version either (without any message). Also it does not work in IE 11.0 & Edge! (both without any message)
Quick answer : append the form to the body.
document.body.appendChild(form);
Or, if you're using jQuery as above
$(document.body).append(form);
Details :
According to the HTML standards, if the form is not associated to the browsing context(document), the form submission will be aborted.
HTML SPEC see 4.10.21.3.2
In Chrome 56, this spec was applied.
Chrome code diff see ## -347,9 +347,16 ##
P.S about your question #1. In my opinion, unlike ajax, form submission causes instant page move.
So, showing 'deprecated warning message' is almost impossible.
I also think it's unacceptable that this serious change is not included in the feature change list. Chrome 56 features - www.chromestatus.com/features#milestone%3D56
if you are seeing this error in React JS when you try to submit the form by pressing enter, make sure all your buttons in the form that do not submit the form have a type="button".
If you have only one button with type="submit" pressing Enter will submit the form as expected.
References:
https://dzello.com/blog/2017/02/19/demystifying-enter-key-submission-for-react-forms/
https://github.com/facebook/react/issues/2093
add attribute type="button" to the button on who's click you see the error, it worked for me.
alternatively include
event.preventDefault();
in your
handleSubmit(event) {
see https://facebook.github.io/react/docs/forms.html
I have found this problem in my React project.
The problem was,
I have set the button type 'submit'
I have set an onClick handler on the button
So, while clicking on the button, the onclick function is firing and the form is NOT submitting, and the console is printing -
Form submission canceled because the form is not connected
The simple fix is:
Use onSubmit handler on the form
Remove the onClick handler form the button itself, keep the type 'Submit'
You must ensure that the form is in the document. You can append the form to the body.
I see you are using jQuery for the form initialization.
When I try #KyungHun Jeon's answer, it doesn't work for me that use jQuery too.
So, I tried appending the form to the body by using the jQuery way:
$(document.body).append(form);
And it worked!
<button type="button">my button</button>
we have to add attribute above in our button element
A thing to look out for if you see this in React, is that the <form> still has to render in the DOM while it's submitting. i.e, this will fail
{ this.state.submitting ?
<div>Form is being submitted</div> :
<form onSubmit={()=>this.setState({submitting: true}) ...>
<button ...>
</form>
}
So when the form is submitted, state.submitting gets set and the "submitting..." message renders instead of the form, then this error happens.
Moving the form tag outside the conditional ensured that it was always there when needed, i.e.
<form onSubmit={...} ...>
{ this.state.submitting ?
<div>Form is being submitted</div> :
<button ...>
}
</form>
I faced the same issue in one of our implementation.
we were using jquery.forms.js. which is a forms plugin and available here. http://malsup.com/jquery/form/
we used the same answer provided above and pasted
$(document.body).append(form);
and it worked.Thanks.
I was able to get rid of the message by using adding the attribute type="button" to the button element in vue.
An example of Mike Ruhlin's answer, I was redirecting with react-router-dom Redirect on form submission.
Placing e.preventDefault() into my submit function removed the warning for me
const Form = () => {
const [submitted, setSubmitted] = useState(false);
const submit = e => {
e.preventDefault();
setSubmitted(true);
}
if (submitted) {
return <Redirect push to={links.redirectUrl} />
};
return (
<form onSubmit={e => submit(e)}>
...
</form>
);
};
export default Form;
Depending on the answer from KyungHun Jeon, but the appendChild expect a dom node, so add a index to jquery object to return the node:
document.body.appendChild(form[0])
Adding for posterity since this isn't chrome related but this was the first thread that showed up on google when searching for this form submission error.
In our case we attached a function to replace the current div html with a "loading" animation on submission - since it occurred before the form was submitted there was no longer any form or data to submit.
Very obvious error in retrospect but in case anyone ends up here it might save them some time in the future.
I have received this error in react.js. If you have a button in the form that you want to act like a button and not submit the form, you must give it type="button". Otherwise it tries to submit the form. I believe vaskort answered this with some documentation you can check out.
if using react and something like formik, the issue seems to be in the onClick handlers in the submit button
You can also solve it, by applying a single patch in the jquery-x.x.x.js just add after " try { rp; } catch (m) {}" line 1833 this code:
if (r instanceof HTMLFormElement &&! r.parentNode) {
r.style.display = "none"; document.body.append (r);
r [p] ();
}
This validates when a form is not part of the body and adds it.
I noticed that I was getting this error, because my HTML code did not have <body> tag.
Without a <body>, when document.body.appendChild(form); statement did not have a body object to append.
Your button has to be in the context of Form tag
button type="submit"
I was also facing the same issue , I removed onClick={onSubmit} form the button tag (I used Formik here)
I saw this message using angular, so i just took method="post" and action="" out, and the warning was gone.

Categories

Resources