Redux Form wrapper for kendo-react-ui Input error - reactjs

I am using react-kendo-ui. I want to wrap Input from #progress/kendo-react-inputs to use it with ReduxForm. Please find my code below:
import React from 'react'
import { Input } from '#progress/kendo-react-inputs';
const InputText = ({ input, label, type, meta: { touched, error } }) => (
<div>
<label>{label}</label>
<div>
<Input {...input} type={type} placeholder={label} />
{touched && error && <span>{error}</span>}
</div>
</div>
)
export default InputText
Call the InputText from another component as below:
import React from 'react';
import { Field, reduxForm } from 'redux-form';
import { Input } from '#progress/kendo-react-inputs';
import InputText from './input-text';
const validateNotEmpty = value => !value ? 'Must enter a value' : null;
const onSubmit = (values) => {
console.log(values);
}
const AddLoc= ({ handleSubmit }) => (
<form onSubmit={handleSubmit}>
<div>
<Field
label="Address"
name="address"
component={InputText}
validate={validateNotEmpty}
/>
</div>
<button type="submit">Submit</button>
</form>
)
export default reduxForm({
form: 'AddLoc'
})(AddLoc)
But while typing inside the input text it keeps giving the following error/warning:
Warning: This synthetic event is reused for performance reasons. If you're seeing this, you're accessing the property `nativeEvent` on a released/nullified synthetic event. This is set to null. If you must keep the original synthetic event around, use event.persist().
While typing inside the input text automatically outputs [object Object]. Please check the image above. Could anyone please let me know what is causing the error.
Thanks

The reduxForm works only with pure DOM <input />. Internally it clones the elements search the children and then attaches the onChange dynamically. So it will not work with the Input and the NumericTextBox from the #progress/kendo-react-inputs package. This statement is based on the official kendo documentation about integrating with the redux-form.
The same author of the redux-form have fork of it called react-final-form which could be used on any component that have Value and onChange props. By our tests it works with the components from #progress/kendo-react-inputs and #progress/kendo-react-dropdowns packages. It looks kendo already have an example using final-form in their integration section.

Related

Receiving 'Function components cannot be given refs' error when applying 'react-input-mask' library to my 'react-form-hook' form component

I'm currently lost on these two error messages I'm receiving. They began to occur when I added 'react-input-masks' library to my code. The code itself works as I expect it to, but I can't figure out why I'm receiving the following errors concerning refs (especially the first one).
My understanding was that my attempt at using refs within a functional component was the culprit and I attempted to fix it by wrapping React.forwardRef() to my component, but this did nothing to fix the problem. I've tried methods to include refs into the controller and InputMask components, but this has not changed the refs. I'm not even sure if the controller is the issue if not for the error message mentioning it.
Below is my code which I have cut down to what seems to be the area of concern. Everything outside of this worked error-free.
import React, {useRef} from 'react';
import { useForm, Controller } from "react-hook-form";
import InputMask from "react-input-mask";
const CreateAccountForm = React.forwardRef((props, ref) => {
const {register, formState: { errors }, handleSubmit, watch, control} = useForm();
const password = useRef({});
password.current = watch("password", "");
const onSubmit = (register) => {
console.log(register)
};
return (
<div className='sign-in-form'>
<label> Date of Birth
<Controller
control={control}
render={({ field: { ref, ...rest} }) => (
<InputMask
{...rest}
mask="99/99/9999"
placeholder="mm/dd/yyyy"
inputRef={ref}
/>
)}
{...register('DOB',
{
required: 'date of birth missing',
pattern:
{
value: /^\d{1,2}\/?\d{1,2}\/?\d{4}$/,
message: 'Invalid Date'
},
})}
name="DOB"
defaultValue=""
type="date"
/>
</label>
<button type="submit">Create Account</button>
</form>
</div>
)
})
export default CreateAccountForm;
Thanks for any help in advance. I tried looking up solutions but struggled with the answers provided elsewhere.
*Edit
As requested, added the error messages as text below.
index.js:1 Warning: Function components cannot be given refs. Attempts to access this ref will fail. Did you mean to use React.forwardRef()?
Check the render method of `ForwardRef`.
at Controller (http://localhost:3000/static/js/vendors~main.chunk.js:30829:35)
at label
at form
at div
at http://localhost:3000/static/js/main.chunk.js:385:70
at SessionContainer
at div
at App
console.<computed> # index.js:1
index.js:1 Warning: findDOMNode is deprecated in StrictMode. findDOMNode was passed an instance of InputElement which is inside StrictMode. Instead, add a ref directly to the element you want to reference. Learn more about using refs safely here: https://reactjs.org/link/strict-mode-find-node
at input
at InputElement (http://localhost:3000/static/js/vendors~main.chunk.js:33079:30)
at Controller (http://localhost:3000/static/js/vendors~main.chunk.js:30829:35)
at label
at form
at div
at http://localhost:3000/static/js/main.chunk.js:385:70
at SessionContainer
at div
at App
console.<computed> # index.js:1
I managed to figure out a solution to removing both of the errors I received (which turns out to not be related, but two distinct issues).
First, the 'findDOMNode is deprecated in StrictMode...' error appears to be an issue with the react-input-mask library. The library is no longer active and an alternative library I found that performs the same action but is still active is react-number-format.
The error concerning 'Function components cannot be given refs' is the result of 'register' within the Controller component. Register is already handled by the component and is not needed. Instead, you should use 'rules' to avoid the forwardRefs error. Here is an example that fixed my code:
import React, {useRef} from 'react';
import { useForm, Controller } from "react-hook-form";
import NumberFormat from 'react-number-format';
const CreateAccountForm = (props, ref) => {
const {register, formState: { errors }, handleSubmit, watch, control} = useForm();
const password = useRef({});
password.current = watch("password", "");
const onSubmit = (register) => {
debugger;
console.log(register)
};
return (
<div className='sign-in-form'>
<label> Date of Birth
<Controller
innerRef={ref}
control={control}
render={({field}) => (
<NumberFormat
{...field}
format="##/##/####"
allowEmptyFormatting
mask="_"
/>
)}
rules={{
required: 'date of birth missing',
pattern:
{
value: /^\d{1,2}\/?\d{1,2}\/?\d{4}$/,
message: 'Invalid Date'
},
}}
name="DOB"
defaultValue=""
type="date"
/>
</label>
<button type="submit">Create Account</button>
</form>
</div>
)
}
export default CreateAccountForm;

React-final-form - input losing focus after every keystroke

import React from "react";
import { Field, Form } from "react-final-form";
export function LogInDialog(props: { open: boolean; onClose: () => void }) {
const onSubmit = vals => {
alert(JSON.stringify(vals));
};
console.log("logindialog");
return (
<Form
key="unique_key_0"
onSubmit={onSubmit}
render={({ handleSubmit, form, submitting, pristine, values }) => (
<form onSubmit={handleSubmit} key="unique_key_1" id="unique_id_1">
<Field
key="unique_key_2"
id="unique_id_2"
name="email"
component={({ input: { onChange, value }, label }) => (
<input
key="unique_key_3"
id="unique_id_3"
type="text"
value={value}
onChange={onChange}
/>
)}
></Field>
</form>
)}
/>
);
}
The input is losing its focus after every keystroke. In devtools, I can see the HTML form is created anew every time (it flashes pink). The React component itself however goes through rendering only once.
There are similar questions, however all of them suggest using unique keys. Such a solution doesn't seem to be working here.
Why is the input losing its focus over and over again? How do I fix it?
https://codesandbox.io/s/busy-torvalds-91zln
Since an inline lambda is used for the component, its identity changes every render.
While according to many other questions an unique key should be enough, moving the component function outside the master component fixes it altogether.

React Hooks - Input loses focus when 1 character is typed in

I'm playing with React Hooks - rewriting a form to use hook concepts. Everything works as expected except that once I type any 1 character into the input, the input loses focus.
I guess there is a problem that the outside of the component doesn't know about the internal changes in the component, but how do I resolve this issue?
Here is the useForm Hook:
import React, { useState } from "react";
export default function useForm(defaultState, label) {
const [state, setState] = useState(defaultState);
const FormComponent = () => (
<form>
<label htmlFor={label}>
{label}
<input
type="text"
id={label}
value={state}
placeholder={label}
onChange={e => setState(e.target.value)}
/>
</label>
</form>
);
return [state, FormComponent, setState];
}
Here is the component that uses the Hook:
function App() {
const [formValue, Form, setFormValue] = useForm("San Francisco, CA", "Location");
return (
<Fragment>
<h1>{formValue}</h1>
<Form />
</Fragment>
);
}
While answer by Kais will solve the symptoms, it will leave the cause unaddressed. It will also fail if there are multiple inputs - which one should autofocus itself on rerender then?
The issue happens when you define a component (FormComponent) inside the scope of another function which is called each render of your App component. This gives you a completely new FormComponent each time your App component is rerendered and calls useState. That new component is then, well, without focus.
Personally I would feel agains returning components from a hook. I would instead define a FormComponent component, and only return state from useForm state.
But, a working example closest to your original code could be:
// useForm.js
import React, { useState } from "react";
// Define the FormComponent outside of your useForm hook
const FormComponent = ({ setState, state, label }) => (
<form>
<label htmlFor={label}>
{label}
<input
type="text"
id={label}
value={state}
placeholder={label}
onChange={e => setState(e.target.value)}
/>
</label>
</form>
);
export default function useForm(defaultState, label) {
const [state, setState] = useState(defaultState);
return [
state,
<FormComponent state={state} setState={setState} label={label} />,
setState
];
}
// App.js
import useForm from "./useForm";
export default function App() {
const [formValue, Form] = useForm("San Francisco, CA", "Location");
return (
<>
<h1>{formValue}</h1>
{Form}
</>
);
}
Here's a sandbox
When you enter any text in input box. Parent Component is also re-rendering. So you need to make focus on input manually.
For this, use autoFocus in input tag
<input
type="text"
id={label}
value={state}
placeholder={label}
onChange={e => setState(e.target.value)}
autoFocus
/>
The above answers didn't work for me. The solution that worked for me was much simpler and, for that reason, less obvious.
The Problem
Essentially, the value that I was changing with the input was also being used for each key in a list of inputs.
Hence, when I updated the value the key would change and React would detect that it's different relative to the last key and create a new input in its place. As a new input it wouldn't focus on itself.
However, by using autoFocus it would automatically focus on the newly created input. But the issue wasn't fixed as it was obvious that the input was continually going through a cycle of un-focus and focus.
Here's an article demonstrating the problem.
The Fix
Update the key to an unchangeable value so React would have a stable reference to the list items. In my case I just updated it to the index. This is not ideal (React docs recommend using a stable ID), but in my situation it was okay because the order of the items won't change.
The first solution actually worked for me , initially the functional component which contained the text field was part of the main functional component , i was facing the same issue but when i extracted the text-field component into another page and imported it it worked fine
This was my component
<Paper >
<div>
<div style={{padding:'10px' ,display:'flex'}} >
<inputstyle={{marginRight:'5px'}} value={val} onChange={(e)=>{setVal(e.target.value)}} />
</div>
</div>
</Paper>

Enzyme Shallow not finding a react-bootstrap Component

Below is a Sign In React SignIn Component made by using Redux-forms
const renderInput = ({input,label,type,placeholder}) => {
return (
<div>
<Form.Label>{label}</Form.Label>
<Form.Control type={type} placeholder={placeholder} { ...input}/>
</div>
)
}
export let signInForm = props => {
const { error,handleSubmit , pristine , submitting } = props
return (
<Container className="justify-content-md-center">
<Alert variant="primary">Sign in here if you already have an account</Alert>
<Form onSubmit={handleSubmit}>
<Form.Group>
<Field name="email" component={renderInput} label="Email" type="email" placeholder="Email" />
</Form.Group>
<Form.Group>
<Field name="password" component={renderInput} label="Password" type="password" placeholder="Password" />
</Form.Group>
<Button type="submit" disabled= { pristine || submitting }>Sign In</Button>
</Form>
</Container>
)
}
export default signInForm = reduxForm ({
form : 'signIn'
})(signInForm)
My enzyme-shallow test for this
import React from 'react';
import { shallow } from 'enzyme';
import {signInForm as SignIn} from './SignIn';
import Button from 'react-bootstrap/Button'
import { expect } from 'chai';
describe('Test SignIn component', () => {
it('Test click event', () => {
const mockCallBack = jest.fn();
let wrapper = shallow(<SignIn onSubmit={mockCallBack}/>);
expect(wrapper.find(Button)).to.have.lengthOf(1);
})
})
My test output says
AssertionError: expected {} to have a length of 1 but got 0
1) The test fails. The Button component is not found in the test. I am expecting it to have a length of 1
2) I am using chai method to.Have.lengthOf because I could not get the jest method toHaveLength to work. toHaveLength seems to be used for only checking arrays or strings size. How could I use jest to do this?
If you are trying to simulate the SignIn form submit, you would actually call the simulate event on the form itself and not on the button.
You can simulate that with this code:
wrapper.find('form').simulate('submit');
Here is some info on why that is from the Enzyme docs:
Currently, event simulation for the shallow renderer does not propagate as one would normally expect in a real environment. As a
result, one must call .simulate() on the actual node that has the
event handler set.
Even though the name would imply this simulates an actual event, .simulate() will in fact target the component's prop based on the
event you give it. For example, .simulate('click') will actually get
the onClick prop and call it.
As noted in the function signature above passing a mock event is optional. Keep in mind that if the code you are testing uses the
event for something like, calling event.preventDefault() or accessing
any of its properties you must provide a mock event object with the
properties your code requires.
https://airbnb.io/enzyme/docs/api/ShallowWrapper/simulate.html

Displaying error messages onChange in redux-form

I'm trying to use redux-form Field (together with Material-ui) and validation. It appears that the error messages are shown when I navigate away from the field (i.e. onBlur). What I would like to achieve is to do validation on the fly as the user types, and display error messages onChange event. How would I go about achieving this behavior?
import { TextField } from 'redux-form-material-ui';
import { Field, reduxForm } from 'redux-form';
const MyForm = (props) => (
<form>
<div className="form-group">
<Field
name="foo"
type="text"
hintText="Foo"
component={TextField}
/>
</form>
);
export default reduxForm({
form: 'MyForm',
validate
})(MyForm);
The default behaviour of validation is that messages are only displayed when fields are touched (i.e. onBlur).
I suppose you could work around that by using the touch method in componentWillMount, so that it becomes touched upon mount:
componentWillMount() {
const { touch } = this.props;
touch('foo');
}
That does however have the drawback of possibly showing any errors directly when the form is loaded, but in this case validation is triggered in onChange right away.
Have you played with the example in CodeSandbox? Like Dennis suggested, you could call "touch", though depending on your need you may choose to do this onChange:
<Field
name="username"
type="text"
component={renderField}
label="Username"
onChange={() => touch('username')}
/>

Resources