Search code examples
reactjsstyled-componentsreact-hook-formyup

Why does react-hook-form consider the select field empty on submission?


Can someone elucidate me as to why upon submission 'category' is considered empty and triggers the invalid call of react form hooks?

I simplified the code as much as possible but without stripping away the involved pieces that could be interacting with each other.

Thanks!

import React from 'react';
import styled from 'styled-components';
import { useForm } from 'react-hook-form';
import * as yup from "yup";
import { yupResolver } from '@hookform/resolvers/yup';

const SelectRoot = styled.div`
    position: relative;
`;

export const SelectOriginal = styled.select`
    width: 100%;
`;

export function Select(props) {
  return (
    <SelectRoot>
      <SelectOriginal {...props} />
    </SelectRoot>
  );
}

const schema = yup
    .object({
        category: yup.string().required(),
    })
    .required();

export default function App() {
  const {
    register,
    handleSubmit,
  } = useForm({ resolver: yupResolver(schema) });

  const onValidSubmit = async (data) => {
        console.log('valid: ', data)
  }

  const onInvalidSubmit = async (errors) => {
        console.log('invalid: ', errors)
  }

  return (
    <div>
      <form onSubmit={handleSubmit(onValidSubmit, onInvalidSubmit)}>
        <Select {...register('category')}>
          <option value="option-1">Option 1</option>
          <option value="option-2">Option 2</option>
        </Select>

        <button type="submit">Submit</button>
      </form>
    </div>
  );
}


Solution

  • The root cause of your issue is this warning, which you can probably see in your console:

    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 `App`.
        at Select
        at form
        at div
        at App
    

    React Hook Form needs to have access to the ref of the select element, which you will need to pass explicitly when wrapping HTML elements in a custom React component, as passing the ref returned by the register method directly to the custom component won't work: ref is a reserved prop name in React.

    const Select = React.forwardRef((props, ref) => {
      return (
        <SelectRoot ref={ref}>
          <SelectOriginal {...props} />
        </SelectRoot>
      )
    })