react-quill loses focus when onChange is called - reactjs

I have a component wrapper in antd Form.create() HOC where I want to implement validation for my react-quill editor.
<Form.Item>
{getFieldDecorator('input', {
rules: [RequiredRule],
initialValue: text,
onChange: this.onChangeText
})(
<ReactQuill
className="question-form__rich-text"
modules={quillToolbar}
/>,
)}
</Form.Item>
If I start typing inside my quill editor text field it triggers the onChangeText function which in its turn changes the local state and initiates rerender of the component
onChangeText = (_, __, ___, editor) => {
this.setState({
textVal: editor.getText(),
});
};
this.state = {
textVal: '',
};
Every time the component rerenders my text field loses focus, so I have to click inside the field to type another letter.
I noticed that it happens only if the <ReactQuill> component is wrapped by antd <Form.Item>
It also shows some weird behaviour if I put console.log inside onChangeText function and try to check what's inside the text field:
Let's say my text field is initially empty. I type letter 'a' and it calls the function 3 times. First, it shows that the text field contains letter 'a', then it calls the function again showing that the field is empty and then the 3rd time letter 'a' appears again. This behaviour persists as I keep typing.
Also, there is an warning saying addRange(): The given range isn't in document. which I have no idea what it means.
I've been struggling with that issue for a few days now, any help would be greatly appreciated. Thank you

The reason it's loses focus when keypress is probably because the quill component is recreating when rerendering. See this same question about losing focus when typing.
I can't find a sample of implementing react quill on antd using getFieldDecorator, so I made a little workaround to make it work but same output. See this working link I made.
const { getFieldDecorator, validateFields, setFieldsValue } = props.form;
const onChangeText = (text) => {
console.log("called");
text = text !== "<p><br></p>" ? text : "";
setFieldsValue({ input: text });
setTextVal(text);
};
<Form.Item>
{getFieldDecorator("input", {
rules: [{ required: true }],
})(<Input.TextArea style={{ display: "none" }} />)}
<ReactQuill
className="question-form__rich-text"
modules={quillToolbar}
defaultValue={textVal}
onChange={onChangeText}
/>
</Form.Item>
as you can see in the above code, I place the editor outside getFieldDecorator and replaced it with hidden <Input> so your set of rules will still applied. The quill component is uncontrolled component and every time it change, it will also change the value of the hidden <Input> so when you submit or get the value of the form, the value of it is the value of quill component.
Also the warning you get addRange(): the given range isn't in the document was not visible on the codesandbox. You may look into this about that.

Related

react MUI TextField inside react-hook-form Controller inside MUI Stepper-Dialog setValue not working

I have a Button that opens a MUI Dialog.
Inside the Dialog I have a MUI Stepper. My Form is split up into different parts. Some Inputs are required others are not.
//Example Input
<Controller
name="stateName"
control={control}
rules={{ required: true }}
render={({ field: { onChange, value } }) => (
<TextField
required
label="stateName"
variant="standard"
onChange={onChange}
value={value}
fullWidth
error={errors.stateName ? true : false}
helperText={errors.stateName ? "Pflichtfeld" : null}
/>
)}
/>
Full Example: https://codesandbox.io/s/gracious-tdd-dkzoqy
When I submit my form I add an entry to an existing list and display it alongside with an edit-Button.
If the edit-Button gets pressed I want to open the Dialog and have the Inputs filled with the values of the edited data.
I tried using react-hook-form setValue("field", value) but it is not working.
I also tried to pass the edit-object via Props to the nested form-steps and use setValue inside these components useEffect utilizing useFormContext() but it didn't work either.
How can I pass the values to the Inputs so they get correctly displayed in the Multi-Step-Form-Dialog?
Working CSB -> https://codesandbox.io/s/eloquent-chaum-znt71c?file=/src/App.tsx
In editHandler, state is a json string, so the simplest fix is to parse it into the object
const editHandler = (stateJSON: any) => {
const state = JSON.parse(stateJSON)
methods.reset(state);
But in submitHandler data is stringified, the submitHanlder should look smth like this:
const submitHandler = (data: any) => {
setContent(prevContent => [...prevContent,data] );
methods.reset();
setEditState(undefined);
setOpen(false);
};
Also checkout this out https://beta.reactjs.org/learn/updating-objects-in-state
and
how to avoid mutations https://www.educative.io/courses/simplifying-javascript-handy-guide/B6yY3r7vEDJ

Focus On Input With Error on Form Submission

It would be nice to have functionality when the user submits a form that fails the validation, it will set focus on the first field with an error.
I have shouldFocusError : true set in the useForm configuration. When I click submit, the validation triggers, but the input is not focused.
Any suggestions on how I can achieve this?
For reference, I am using MUI and react-hook-form to create custom components in the form. For the components themselves, they are using the useController hook like so:
const { field, fieldState } = useController(props);
<TextField
{...field}
error={!!fieldState.error}
helperText={fieldState.error?.message || ''}
/>
Solution:
Spreading the field object returned from the useController hook contains the following:
{
value: ...
name: ...
onChange: ...
onBlur: ...
ref: ...
}
The problem I described above was solved when I removed ref property from the object and passed it separately to inputRef. Like so:
const { field, fieldState } = useController(props);
const { ref, ...fieldProps } = field;
<TextField
{...fieldProps}
inputRef={ref} //or you can use field.ref
id={field.name} //this solved other issues I was having
error={!!fieldState.error}
helperText={fieldState.error?.message || ''}
/>
After doing this, React Hook Form set focus on the first field with an error when the user submits a form that fails the validation.

Changing Autocomplete value using react-hook-form (Material-UI)

I have two Autocomplete fields, my goal is to change the value of the second field by the value of the first field.
The problem I face is when my trying to send the new value to the "setValue" function nothing happens the state form changing, but the autocomplete field shows the old value.
In this sand box:
https://codesandbox.io/s/dazzling-carson-84dxp?file=/src/Form/Components/UserCountry.js
you can see my implementation.
If you look at console when you change user_name field, you can see materialUI warning in the console which says:
Material-UI: A component is changing the uncontrolled value state of Autocomplete to be controlled.
it's reason is that your user_country's value by default is undefined and material consider this field as uncontrolled field, it means material will take the control of user_country value. To solve the problem you have two solutions:
1- Defining your form by defaultValues options like this:
const methods = useForm({
defaultValues: {
user_name: null,
user_country: null
}
});
2- Sending null as value to AutoComplete when it doesn't have any value, like this:
<Controller
control={control}
name={`user_country`}
render={({ field: { onChange, value } }) => (
<Autocomplete
id="country-select-demo"
options={countries}
onChange={(event, item) => {
onChange(item);
}}
value={value || null}
...
/>
)}
/>
Here You can see your form which updates user_country whenever you change user_name field.

Is there a standard design pattern for viewing + editing a record in React / Material-UI?

Frequently I have a record of data to display to the user, and I want to display an Edit button allowing that data to be modified:
Create Pizza:
sauce: Margherita
cheese: Gorgonzola
radius: 12
[EDIT] [ORDER]
Is there a standard pattern for this in React / Material-UI? It seems silly to implement the same view twice, once with (readonly) <div>s or whatever and once with <input>s. Is there a better way? (Perhaps there's a Material-UI component with an "editable" prop, for instance.)
Take a look at api for text field. You'll see that you have a couple of ways of doing what you are looking.
You could set disabled={true}
You also get inputProps, as an example
<TextField id="time" type="time" inputProps={{ readOnly:true }} />
All the other elements should also have one or the other or both.
I usually have a wrapper component that accepts props such as ({isReadOnly: boolean}) & within that is the "native" React MUI component & then I send back editable or a read only component back to the caller.
EDIT:
interface MyTextFieldProps extends StandardTextFieldProps {
isEditable?: boolean;
// send in any other props necessary to render your non editable
// portion
}
const MyTextField: FC<MyTextFieldProps> = (props) => {
const { isEditable, ...other } = props;
return <>{props.isEditable ? <TextField {...other}></TextField> : <span>text:{props.value}</span>}</>;
};

React Native TextInput has no focus method

I've been digging around and I can't seem to find a proper way to focus the next TextInput in a form. I'm using React Native 0.61.5 & React 16.9.0 and have set-up the project with a Typescript template. Most of what I've found online uses earlier versions of React Native or React and I can't downgrade because it's a company project and what versions we use are up to the dev leads.
React Native's documentation https://facebook.github.io/react-native/docs/textinput#__docusaurus doesn't show a .focus() method to exist on TextInput. There's onFocus, but that occurs after the TextInput has been focused, it doesn't help us set the focus to a particular TextInput on the screen after hitting the return key.
Using refs is a sound idea:
inputRef = React.createRef<'TextInput>(); // (comma after < because it kept hiding the code)
Intellisense for hovering over inputRef:
(property) LoginForm.inputRef: React.RefObject<'TextInput> // (comma after < because it kept hiding the code)
I'm using this ref like this:
this.inputRef?.current?.focus();
but I keep getting a typescript error saying:
Property 'focus' does not exist on type 'TextInput'.
Which, given the documentation, makes sense since I couldn't find it as a property there.
Intellisense for TextInput when hovering over ref attribute:
(JSX attribute) React.ClassAttributes.ref?: string | ((instance: TextInput | null) => void) | React.RefObject | null | undefined
I want to be able to tap the return key on the android/ios virtual keyboard and have the focus shift to the next TextInput so that I can submit the form.
The object create with React.createRef() have the following shape
{ current: TextInput }
You can access the text input element by doing:
inputRef.current.focus();
<TextInput
placeholder = "FirstTextInput"
returnKeyType = { "next" }
onSubmitEditing={() => { this.secondTextInput.focus(); }}
blurOnSubmit={false}
/>
<TextInput
ref={input => { this.secondTextInput = input; }}
placeholder = "secondTextInput"
/>

Resources