react-spectrum
react-spectrum copied to clipboard
Input onChange does not expose native event
🐛 Bug Report
Input onChange does not expose native event. This is bad as it does not enable access to the native event which ultimately allows for complex cursor tracing when formatting text fields.
🤔 Expected Behavior
https://github.com/adobe/react-spectrum/blob/main/packages/%40react-aria/textfield/src/useTextField.ts#L141
This ^^^ should be passing e instead of e.target.value
. As this would be huge breaking change. I suggest you simply pass e
as a second parameter so users can use it.
😯 Current Behavior
Native e
event is not passed
💁 Possible Solution
I suggest you simply pass e
as a second parameter so users can use it.
onChange: (e: ChangeEvent<HTMLInputElement>) => onChange(e.target.value, e),
🔦 Context
I am unable to do cursor tracking to format user input.
https://teslamotors.github.io/informed/?path=/story/formatting--number-formatter
Above is example of where compex cursor tracking is used when formatting input fields.
Mind expanding on what cursor tracking is being done in the example you linked? Could you use onInput
or onBeforeInput
instead?
EDIT: ah is it to calculate where to place the cursor when a comma is added/removed?
This has been discussed before: #1860.
TL;DR: There are two reasons:
a) Consistency. All of our components expose a value as part of the onChange
event, not a native event. In some components, there is no native event that would make sense.
b) Cross platform support. Our stately hooks are designed to work across platforms with the same API, e.g. web and react native.
But, that's not to say it's impossible to do what you want. With React Aria, you control the DOM structure, so it's easy to override props we return from our hooks where needed. In this example, we chain React Aria's handler with our own.
import {useTextField} from '@react-aria/textfield';
import {chain} from '@react-aria/utils';
function MyInput(props) {
let {inputProps} = useTextField(props);
let myOnChange = e => { /* ... */ };
return <input {...inputProps} onChange={chain(inputProps.onChange, myOnChange)} />
}
@LFDanLu Yes it is for setting the position of cursor when the commas are added. @devongovett Hmm so the hook basically turns a basic DOM input into a react spectrum input ? It seems to me I would be missing out on all the goodies in here tho .. https://github.com/adobe/react-spectrum/blob/main/packages/%40react-spectrum/textfield/src/TextField.tsx
Specifically the TextFieldBase
... that has a bunch of things I want to keep. going the custom route just so I can get the native e event seems to throw half of the benefits away ? correct me if im wrong.
Ah, didn't realize you were using React Spectrum - assumed you were using the React Aria hooks with your own design.
In that case, you might be able to do what you want with a ref? TextField's ref exposes a getInputElement()
function.
let ref = useRef();
let onChange = () => {
let input = ref.current.getInputElement();
// do stuff.
};
<TextField onChange={onChange} />
Ahh a hack but might work non the less! Thanks!
PS: I might make a wrapper library or even use react-spectrum as the main design system for my form docs. Its one of the better design systems I have ever used!
Ok so now im having issues because I think you guys are doing some fancyness with the ref here https://github.com/adobe/react-spectrum/blob/main/packages/%40react-spectrum/textfield/src/TextFieldBase.tsx
Im getting
inputRef.current.setSelectionRange is not a function
Internally to my form library i check if they user has passed a DOM ref and call setSelectionRange
.. problem is I cant go changing my library to call a getInputElement
as that is custom logic to react-spectrum. I wish I could just access the real ref 🤔 any ideas?
A really easy solve for this is to allow the user to pass their own inputRef
let inputRef = useRef<HTMLInputElement>();
inputRef = userInputRef ?? inputRef;
https://github.com/adobe/react-spectrum/blob/main/packages/%40react-spectrum/textfield/src/TextField.tsx#L22
I do same thing here
https://github.com/teslamotors/informed/blob/master/src/hooks/useField.js#L161
Following up with this @devongovett as its still a blocker for advanced cursor positioning when formatting fields
Is there a way to do this check in the onChange
of the confirmPassword
field using react-aria-components components?
'use client'
import { useFormState } from 'react-dom'
import { Button, FieldError, Form, Input, Label, TextField } from 'react-aria-components'
import { createUser } from './actions'
export function AddForm() {
let [{ nonFieldErrors, errors }, formAction] = useFormState(createUser, { nonFieldErrors: null, errors: {} })
return (
<Form action={formAction} validationErrors={errors}>
{nonFieldErrors &&
<div role="alert" tabIndex={-1} ref={e => e?.focus()}>
<h3>Unable to submit</h3>
<p>{nonFieldErrors}</p>
</div>
}
<TextField name="name" isRequired>
<Label>Name</Label>
<Input />
<FieldError />
</TextField>
<TextField name="email" type="email" isRequired>
<Label>Email</Label>
<Input />
<FieldError />
</TextField>
<TextField
name="phone"
type="tel"
isRequired
pattern="\((\d{2})\) (\d?)(\d{4})-(\d{4})"
>
<Label>Phone</Label>
<Input />
<FieldError />
</TextField>
<div>
<label htmlFor="id-password">Password</label>
<input type="password" id="id-password" name="password" required />
</div>
<div>
<label htmlFor="id-confirm-password">Confirm password</label>
<input
type="password"
id="id-confirm-password"
name="confirmPassword"
required
onChange={(e) => {
const field = e.currentTarget
const password = field.form.password
if (field.value !== password.value) {
field.setCustomValidity('Passwords do not match')
} else {
field.setCustomValidity('')
}
}}
/>
</div>
<Button type="submit">Add</Button>
</Form>
)
}