Ref with custom class - reactdate picker - reactjs

I am trying to set focus on react datepicker when enter is pressed on earlier component(input filed).
I read a official documentation, and example with simple field is worked, but when I change with DatePicker I get a old error
TypeError: this.inputAccountDate.focus is not a function
My function is:
function CustomDatePicker(props) {
return (
<div>
{/* <input ref={props.innerRef} /> */}
<DatePicker className="form-control"
placeholderText="Izaberite datum"
dateFormat="dd/MM/yyyy"
maxDate={new Date()}
ref={props.innerRef}
/>
</div>
);
}
And snippet code from class with form is:
<Col className="pr-md-1" md="3">
<FormGroup >
<label>Datum računa</label>
<div>
<CustomDatePicker
innerRef={(input) => { this.inputAccountDate = input }}
/>
</div>
</FormGroup>
</Col>
Component where I call a function to set focus is
<Col className="pr-md-1" md="3">
<FormGroup>
<label>Broj računa</label>
<Input style={{'borderColor':'lightgray', 'fontSize':'14px'}}
innerRef={(input) => { this.inputAccountNumber = input }}
onKeyDown={this.focusAccountDate}
placeholder="Br.računa"
type="text"
value={this.state.accountNumber || (header === null ? "" :
header.account_number) }
onChange={this.changeAccountNumber}
onFocus={(e)=>e.target.select()}
required
/>
</FormGroup>
</Col>
Function which managing a focus is
focusAccountDate = (e) => {
if(e !== undefined) {
if(e.key === 'Enter') {
this.inputAccountDate.focus()
}
}
}
and inputAccountDate is this.inputAccountDate = React.createRef()

Use setFocus() instead of focus. There is no focus in DatePicker
In functional Component, the passing ref reference with props is not a valid way.
You need to know Forwarding Ref
Your CustomDatePicker should be changed like the following.
function CustomDatePicker(props, ref) {
return (
<div>
<DatePicker className="form-control"
placeholderText="Izaberite datum"
dateFormat="dd/MM/yyyy"
maxDate={new Date()}
ref={ref}
/>
</div>
);
}
export default React.forwardRef(CustomDatePicker)

Related

React js show/hide specific element using Material UI checkbox's state

hello I'm trying to show and hide specific elements with checkbox material and what is happening now is when one checkbox is checked all the hidden divs are showing up.
You can see the problem here: https://stackblitz.com/edit/react-1ecdqb?file=demo.tsx
edit: I know that I need more variables in the state but I ask if there is a way to do it without state for each checkbox because there are gonna be 10 more checkboxes
const UninstallView = () => {
const [isChecked, setIsChecked] = useState(false);
const handleChange = event => {
if (event.target.checked) {
setIsChecked(true);
}
else {
setIsChecked(false);
}
}
return (
<div>
<FormGroup>
<FormControlLabel control={<Checkbox onChange={handleChange} />} label="simple isn't what I expected" />
{isChecked ? <TextField
id="filled-multiline-static"
label="What did you expect from simple?"
multiline
rows={4}
defaultValue=""
variant="filled"
/>
: '' }
</FormGroup>
<FormGroup>
<FormControlLabel control={<Checkbox onChange={handleChange} />} label="simple isn't working correctly" />
{isChecked ?
<div>
<h1>hello</h1>
</div>
: '' }
</FormGroup>
</div>
);
You are sharing 1 state across 2 checkboxes. You should have a separate state that holds the state for each checkbox.
This code may help:
const UninstallView = () => {
const [isFirstChecked, setIsFirstChecked] = useState(false);
const [isSecondChecked, setIsSecondChecked] = useState(false);
return (<div>
<FormGroup>
<FormControlLabel
control={<Checkbox onChange={() => setIsFirstChecked(!isFirstChecked)}/>}
label="simple isn't what I expected"/>
{isFirstChecked ? <TextField
id="filled-multiline-static"
label="What did you expect from simple?"
multiline
rows={4}
defaultValue=""
variant="filled"
/> : ''}
</FormGroup>
<FormGroup>
<FormControlLabel
control={<Checkbox onChange={() => setIsSecondChecked(!isSecondChecked)}/>}
label="simple isn't working correctly"/>
{isSecondChecked ? <div>
<h1>hello</h1>
</div> : ''}
</FormGroup>
</div>);
}
Sandbox example

Register third party- custom component with react-hook-form

I am using react-hook-form and using third party DatePicker. Since it's a custom component using it as a controlled component to register it. This works fine
<Controller
control={control}
name="reviewStartDate"
render={({ field: { onChange, onBlur, value } }) => (
<DatePicker
className={`form-control ${errors.reviewStartDate ? 'is-invalid' : ''}`}
customInput={<input />}
wrapperClassName="datePicker"
onChange={onChange}
onBlur={onBlur}
selected={value ? new Date(value) : ''}
dateFormat='dd-MMM-yyyy'
/>
)}
/>
Similarly/however, I am using thirdparty Multiselect. Here the value is not being registered. It does show the selected value but when I submit the form the value is not present in data.
<Controller
control={control}
name="rootCauseAnalysisCategory"
render={({ field: { value } }) => (
<Multiselect
options={rootCauseAnalysisCategorys}
isObject={false}
showCheckbox={true}
hidePlaceholder={true}
closeOnSelect={false}
selectedValues={value}
/>
)}
/>
Similarly
The <MultiSelect /> component has onSelect and onRemove props, so you can just pass onChange to them. This will work because they both have the signature that the first argument is an array containing the current selected values.
<Controller
control={control}
name="rootCauseAnalysisCategory"
defaultValue={[]}
render={({ field: { value, onChange } }) => (
<Multiselect
options={rootCauseAnalysisCategorys}
isObject={false}
showCheckbox={true}
hidePlaceholder={true}
closeOnSelect={false}
onSelect={onChange}
onRemove={onChange}
selectedValues={value}
/>
)}
/>
UPDATE
If you want to access the current value for rootCauseAnalysisCategory, you have to use watch. Please note, that it is also important to either provide a defaultValue at the <Controller /> field level or call useForm with defaultValues. In the example i passed the defaultValue at the field level.
function App() {
const { control, handleSubmit, watch } = useForm();
const onSubmit = (data) => {
console.log(data);
};
const rootCauseAnalysisCategorys = ["Category 1", "Category 2"];
const rootCauseAnalysisCategory = watch("rootCauseAnalysisCategory");
return (
<div className="App">
<form onSubmit={handleSubmit(onSubmit)}>
<Controller
control={control}
name="rootCauseAnalysisCategory"
defaultValue={[]}
render={({ field: { value, onChange } }) => (
<Multiselect
options={rootCauseAnalysisCategorys}
isObject={false}
showCheckbox={true}
hidePlaceholder={true}
closeOnSelect={false}
onSelect={onChange}
onRemove={onChange}
selectedValues={value}
/>
)}
/>
{rootCauseAnalysisCategory?.includes("Category 1") && <p>Category 1</p>}
<input type="submit" />
</form>
</div>
);
}

react-hook-form isDirty seems weird for me

Today, I started to use react-hook-form and the isDirty variable seems quite weird for me.
It is always true although only the focus was given to any input elements.
I expect isDirty should be true only when value of input element changes. Is that normal in react-hook-form?
// I had to make workaround like this. but I am not sure if this is normal for everybody.
const closeForm = () => {
const { dirtyFields } = form.formState
const isReallyDirty = Object.keys(dirtyFields).length > 0
if (isReallyDirty) {
if (window.confirm("Discard the changes?")) {
dispatch(closeItem())
}
} else {
dispatch(closeItem())
}
}
UPDATE: I think this is a bug of react-hook-form?
react-hook-form version 6.11.0
This happens only when React.forwardRef was used.
const TextareaBox = ({ ref, ...props }) => {
const { errors, name } = props
const { required, ...restProps } = props
return (
<Row>
<Label {...props} columnSize={2} />
<Col lg={10}>
<textarea id={name} {...restProps} maxLength="200" rows="3" ref={ref} />
<ErrorMessage className="errorMessage" errors={errors} name={name} as="p" />
</Col>
</Row>
)
}
const TextareaBox = React.forwardRef((props, ref) => {
const { errors, name } = props
const { required, ...restProps } = props
return (
<Row>
<Label {...props} columnSize={2} />
<Col lg={10}>
<textarea id={name} {...restProps} maxLength="200" rows="3" ref={ref} />
<ErrorMessage className="errorMessage" errors={errors} name={name} as="p" />
</Col>
</Row>
)
})
I had a similar issue and I ended up solving it by checking length of dirtyFields property of the formState.
In react hook form, you may feel that isDirty behaves more like it is isTouched. But you have to pass the defaultValue to the input field because RHF needs a value to compare against as mentioned in the official documents.
Let me know if that makes sense.

Using react-datepicker alongside redux-form?

I'm trying to use redux-form along with react-datepicker but keep getting the following error:
Warning: Failed prop type: Invalid prop `value` of type `object` supplied to `DatePicker`, expected `string`.
I have my code setup as follows:
renderDatePicker({input, placeholder, defaultValue="01/01/2018", meta: {touched, error} }) {
return (
<div>
<DatePicker {...input} dateForm="MM/DD/YYYY" selected={input.value ? moment(input.value) : null} />
{touched && error && <span>{error}</span>}
</div>
)
};
render() {
const { handleSubmit } = this.props;
return (
<form onSubmit={handleSubmit(this.submit.bind(this))}>
<Field name='budgetDateDue' component={this.renderDatePicker} format={(value, name) => value || null} />
<button type='submit'>Submit</button>
</form>
)
}
I can't seem to get around this Warning and I haven't found much online aside from what I have already done.
input is an object, and input.value is a moment object. <DatePicker /> is expecting a string, so you'll need to pass it one. Convert the moment object to a string (and format it however you want).
<DatePicker
{...input}
value = {moment(input.value).format('MM-YYYY')}
dateForm = "MM/DD/YYYY"
selected = {input.value ? moment(input.value) : null}
/>
You can create a custom datepicker and pass props to it
const datePicker = ({ input, label, type, className, selected, meta: { touched, error } }) => (
<div>
<div>
<DatePicker {...input}
selected={selected}
placeholder={label}
type={type}
className={className}
dropdownMode="select"
/>
{touched && error && <span className="error_field">{error}</span>}
</div>
</div>
)
And in redux-form component you can do something like this
<Field
name="date_of_birth"
component={datePicker}
type="text"
selected={this.state.date_of_birth}
onChange={this.handleChange.bind(this)}
className="form-control"
/>
Any of the samples below worked for me, this was my solution:
class DateField extends Component {
render() {
const {
input,
meta: { error, touched },
} = this.props;
return (
<div>
<DatePicker
{...input}
dateForm="MM/DD/YYYY"
selected={input.value && input.value}
onChange={time => {
input.value = time ? time : null;
}}
onBlur={() => input.onBlur(input.value)}
/>
{touched && error && <span className="error-block">{error}
</span>}
</div>
);
}
}
export default DateField;
Then wrap this in a Form element.

How to get an input value using "refs" in react-bootstrap form?

I'm trying to create a form that appears in modal. So when user input a value, that value is stored in local storage. Here's a picture that help's you to understand what I mean:
Here is the code of the form:
function FieldGroup({id, label, help, ...props}) {
return (
<ReactBootstrap.FormGroup controlId={id}>
<ReactBootstrap.ControlLabel>{label}</ReactBootstrap.ControlLabel>
<ReactBootstrap.FormControl {...props} />
{help && <ReactBootstrap.HelpBlock>{help}</ReactBootstrap.HelpBlock>}
</ReactBootstrap.FormGroup>
);
}
const formInstance = (
<form>
<FieldGroup
id="formControlsText"
type="text"
label="Text"
placeholder="Recipe Name"
inputRef={ref => { this.input = ref; }}
/>
<ReactBootstrap.FormGroup controlId="formControlsTextarea">
<ReactBootstrap.ControlLabel>Ingredients</ReactBootstrap.ControlLabel>
<ReactBootstrap.FormControl componentClass="textarea" placeholder="Enter Ingredients" />
</ReactBootstrap.FormGroup>
</form>
);
As I've read in bootstrap React tutorial, I should add
<FormControl inputRef={ref => { this.input = ref; }} /> to the FormControl props. But after adding it I get an error when modal form is invoked:
`
I just made this issue. My code:
<FormControl
componentClass="input"
placeholder="Enter recipe title"
inputRef={(ref) => {this.input = ref}}
defaultValue={title}/>
</FormGroup>
And then you can get the value from <FormControl> in some handler like this:
console.log(this.input.value);
Details can be found in my repo: https://github.com/kerf007/recipebook
I have same problem with you, and this is my solution
const FieldGroup = ({id, label, help, inputRef, ...props}) =>
<FormGroup controlId={id}>
<ControlLabel>{label}</ControlLabel>
<FormControl {...props} inputRef={inputRef}/>
{help && <HelpBlock>{help}</HelpBlock>}
</FormGroup>
and my form
<form>
<FieldGroup
id="bookName"
type="text"
label="Name"
placeholder="Enter name..."
inputRef = {(input) => this.inputName = input }
/>
<FieldGroup
id="bookAuthor"
label="Author"
type="text"
placeholder="author 's name..."
inputRef={(input) => this.inputAuthor = input}
/>
</form>
then you can get book 's name and author 's name value by:
this.inputName.value and this.inputAuthor.value
This issue (or more like a change in the way it works) is related to React-Bootstrap. The way you're doing it won't work anymore.
The <FormControl> component directly renders the or other specified component. If you need to access the value of an uncontrolled <FormControl>, attach a ref to it as you would with an uncontrolled input, then call ReactDOM.findDOMNode(ref) to get the DOM node. You can then interact with that node as you would with any other uncontrolled input.
Here's an example:
var React = require('react');
var ReactDOM = require('react-dom');
var FormControl = require('react-bootstrap').FormControl;
React.createClass({
render: function() {
return (<FormControl ref="formControl" />);
},
getFormControlNode: function() {
// Get the underlying <input> DOM element
return ReactDOM.findDOMNode(this.refs.formControl);
}
});
As soon as you get the DOM element, you will be able to retrieve the value: this.getFormControlNode().value or do anything else that you want.
PS: Here's a related github issue on this topic.
This worked for me, using https://reactjs.org/docs/refs-and-the-dom.html
constructor(props) {
super(props);
this.email = React.createRef();
}
submit() {
var email = this.email.current.value;
console.log(email);
}
render() {
return (
<Form>
<Form.Control type="email" placeholder="Your email" ref={this.email} />
<Button variant="primary" onClick={()=>this.submit()}>Send</Button>
</Form>
);
}
I think what it suggests to use is the ref callback attribute, so just change inputRef to ref.
FYI: https://facebook.github.io/react/docs/refs-and-the-dom.html
Hello this solution worked for me!
<Form
noValidate
validated={validated}
onSubmit={(e) => this.handleSubmit(e)}
style={{ width: '100%' }}
>
<Form.Group controlId="formBasicEmail">
<Form.Label>Email address</Form.Label>
<Form.Control type="email" placeholder="Enter email" inputRef={(ref) => { this.email = ref }} required />
<Form.Text className="text-muted"> Well never share your email with anyone else.
</Form.Text>
</Form.Group>
</Form>
handleSubmit(event) {
console.log(event.target.elements.formBasicPassword.value)
}
I think I found how to get the ref from React-Bootstrap <Form/>.
import React, {createRef} from 'react'
interface definedProps {}
interface definedState {
myRef: Object //I didn't found the more accurate type
}
export default class SomeClass extends React.Component<definedProps,definedState> {
state = {
myRef: React.createRef<Form<any>>() //That's it!
}
const handleClose = () => {
console.log('this.state.myRef: ', this.state.myRef); //Here we can take data from Form
debugger; //todo: remove
}
render() {
return (
<div>
<Form ref={this.state.myRef}> { /*Here we're connecting the form's ref to State*/}
<Form.Group controlId='formName'>
<Form.Control
type='text'
placeholder='Enter Name'
/>
</Form.Group>
...
<Button
variant='primary'
onClick={handleClose}
>
Save Changes
</Button>
</Form>
</div>
)
}
}

Resources