Mel
Mel

Reputation: 2685

Setting the value of DatePicker (from antd) in react-hook-form

I'm trying to figure out how to use the DatePicker from antd with react-hook-form.

Currently, my attempt is:

import React, { useState, useEffect } from "react";
import ReactDOM from "react-dom";
import useForm from "react-hook-form";
import { withRouter } from "react-router-dom";
import { useStateMachine } from "little-state-machine";
import updateAction from "./updateAction";
import { Input as InputField, Form, Button, DatePicker, Divider, Layout, Typography, Skeleton, Switch, Card, Icon, Avatar } from 'antd';
import Select from "react-select";


const { Content } = Layout 
const { Text, Paragraph } = Typography;
const { Meta } = Card;
const { MonthPicker, RangePicker, WeekPicker } = DatePicker;



  const Team = props => {
    const { register, handleSubmit, setValue, errors } = useForm();
    const [ dueDate, setDate ] = useState(new Date());
    const [indexes, setIndexes] = React.useState([]);
    const [counter, setCounter] = React.useState(0);
    const { action } = useStateMachine(updateAction);
    const onSubit = data => {
      action(data);
      props.history.push("./ProposalBudget");
    };

    
    // const handleChange = dueDate => setDate(date);
    const handleChange = (e) => {
      setValue("dueDate", e.target.value);
    }


  const onSubmit = data => {
    console.log(data);
  };

  const addMilestone = () => {
    setIndexes(prevIndexes => [...prevIndexes, counter]);
    setCounter(prevCounter => prevCounter + 1);
  };

  const removeMilestone = index => () => {
    setIndexes(prevIndexes => [...prevIndexes.filter(item => item !== index)]);
  };

  const clearMilestones = () => {
    setIndexes([]);
  };

  useEffect(() => {
    register({ name: dueDate }); // custom register antd input
  }, [register]);

Note: i have also tried name: {${fieldName}.dueDate - that doesn't work either.

  return (
    <div>
        <HeaderBranding />
        <Content
          style={{
            background: '#fff',
            padding: 24,
            margin: "auto",
            minHeight: 280,
            width: '70%'
          }}
        >
        <form onSubmit={handleSubmit(onSubit)}>
        
          {indexes.map(index => {
            const fieldName = `milestones[${index}]`;
            return (
              <fieldset name={fieldName} key={fieldName}>
                <label>
                  Title:
                  <input
                    type="text"
                    name={`${fieldName}.title`}
                    ref={register}
                  />
                </label>

                <label>
                  Description:
                  <textarea
                    rows={12}
                    name={`${fieldName}.description`}
                    ref={register}
                  />
                </label>
                <label>When do you expect to complete this milestone? <br />
                
                  
                <DatePicker 
                  selected={ dueDate } 
                  // ref={register}
                  InputField name={`${fieldName}.dueDate`} 
                  onChange={handleChange(index)}

                  //onChange={ handleChange }
                   >
                  <input 
                  type="date" 
                  name={`${fieldName}.dueDate`} 
                  inputRef={register}
                />
                </DatePicker>
              
                </label>
                
                <Button type="danger" style={{ marginBottom: '20px', float: 'right'}} onClick={removeMilestone(index)}>
                  Remove this Milestone
                </Button>
              </fieldset>
            );
          })}

          <Button type="primary" style={{ marginBottom: '20px'}} onClick={addMilestone}>
            Add a Milestone
          </Button>
          <br />
          <Button type="button" style={{ marginBottom: '20px'}} onClick={clearMilestones}>
            Clear Milestones
          </Button>
          <input type="submit" value="next - budget" />
        </form>
       </Content>
       
      </div>  
  );
};

export default withRouter(Team);

This generates an error that says: TypeError: Cannot read property 'value' of undefined

setValue is defined in handleChange.

I'm not clear on what steps are outstanding to get this datepicker functioning. Do I need a separate select function?

Has anyone figured out how to plug this datepicker in?

I have also tried:

const handleChange = (e) => {
      setValue("dueDate", e.target.Date);
    }

and I have tried:

const handleChange = (e) => {
      setValue("dueDate", e.target.date);
    }

but each of these generations the same error

Upvotes: 4

Views: 15598

Answers (3)

Massimo Alvaro
Massimo Alvaro

Reputation: 89

/* eslint-disable react/prop-types */
import React, { useState } from 'react';
import { DatePicker } from 'antd';
import { Controller } from 'react-hook-form';
import color from '../../assets/theme/color';

import DatePickerContainer from './DatePickerContainer';

function DatePickerAntd(props) {

  const { control, rules, required, title, ...childProps } = props;
  const { name } = childProps;

  const [focus, setFocus] = useState(false);

  const style = {
    backgroundColor: color.white,
    borderColor: color.primary,
    borderRadius: 5,
    marginBottom: '1vh',
    marginTop: '1vh',
  };

  let styleError;
  if (!focus && props.error) {
    styleError = { borderColor: color.red };
  }

  return (
    <div>
      <Controller
        as={
          <DatePicker
            style={{ ...style, ...styleError }}
            size="large"
            format="DD-MM-YYYY"
            placeholder={props.placeholder || ''}
            onBlur={() => {
              setFocus(false);
            }}
            onFocus={() => {
              setFocus(true);
            }}
            name={name}
          />
        }
        name={name}
        control={control}
        rules={rules}
        onChange={([selected]) =>  ({ value: selected })}

      />
    </div>
  );
}
export default DatePickerAntd;

my container parent use react-hooks-form

  const { handleSubmit, control, errors, reset, getValues } = useForm({
    mode: 'onChange',
    validationSchema: schema,
  });

            <DatePickerAntd
              name="deadline"
              title={messages.deadline}
              error={errors.deadline}
              control={control}
              required={isFieldRequired(schema, 'deadline')}
            />

like that, its working for me ;-)

Upvotes: 4

Bill
Bill

Reputation: 19248

I have built a wrapper component to work with external controlled component easier: https://github.com/react-hook-form/react-hook-form-input

import React from 'react';
import useForm from 'react-hook-form';
import { RHFInput } from 'react-hook-form-input';
import Select from 'react-select';

const options = [
  { value: 'chocolate', label: 'Chocolate' },
  { value: 'strawberry', label: 'Strawberry' },
  { value: 'vanilla', label: 'Vanilla' },
];

function App() {
  const { handleSubmit, register, setValue, reset } = useForm();

  return (
    <form onSubmit={handleSubmit(data => console.log(data))}>
      <RHFInput
        as={<Select options={options} />}
        rules={{ required: true }}
        name="reactSelect"
        register={register}
        setValue={setValue}
      />
      <button
        type="button"
        onClick={() => {
          reset({
            reactSelect: '',
          });
        }}
      >
        Reset Form
      </button>
      <button>submit</button>
    </form>
  );
}

try this out, let me know if it makes your life easier with AntD.

Upvotes: 4

Jarvan
Jarvan

Reputation: 1210

Try this:

        <DatePicker 
          selected={ dueDate } 
          // ref={register}
          InputField name={`${fieldName}.dueDate`} 
          onChange={()=>handleChange(index)}
          //onChange={ handleChange }
           >
          <input 
          type="date" 
          name={`${fieldName}.dueDate`} 
          inputRef={register}
        />

It looks like if you are using onChange={handleChange(index)} it does not pass a function instead you are passing an execution result of that function.

And if you are trying to access event inside handleChange, you should manually pass if from binding scope otherwise, it will be undefined.

onChange={()=>handleChange(index, event)}

Upvotes: -1

Related Questions