redwoodjs / redwood

The App Framework for Startups
https://redwoodjs.com
MIT License
17.31k stars 994 forks source link

[RFC]: FieldError not being populated with server errors when inside Controller when using custom UI components #10737

Open irg1008 opened 4 months ago

irg1008 commented 4 months ago

What's not working?

I am using a react ui library with an Input component. For me to use the lib component I need to use the Controller element. This way I am able to add client validation and pass the props down to my custom component. This works and the client errors show, but then the server errors for the input name doesn't

For example:

<Controller
  name="code"
  rules={{ ... }}
  render={({ field, formState: { error, invalid } }) => ( // ❌ Here the error is client only. Invalid as well
    <div>
      <MyAwesomeUIInput {...field} />
      <FieldError name="code" /> // ✅ Client errors ❌ Server errors
    </div>
  )}
/>

Compared to

<NumberField
  name="code"
  validation={{ ... }}
/>
<FieldError name="code" className="rw-field-error" /> // ✅ Client errors ✅ Server errors

When the NumberField (or any other type of field) is present, the field is correctly populated. I understant that this is intented behaviour. But would be nice for the controller to pick up server error as well to pass to the custom input

How do we reproduce the bug?

No response

What's your environment? (If it applies)

No response

Are you interested in working on this?

irg1008 commented 4 months ago

I fixed it by making a custom wrapper for the react-hook-form controller like:

import { ComponentProps, useContext, useEffect } from 'react'

import {
  Controller as ReactHookFormController,
  ServerErrorsContext,
  useFormContext,
} from '@redwoodjs/forms'

const ServerAwareController = (
  props: ComponentProps<typeof ReactHookFormController>
) => {
  const { name } = props

  const { setError } = useFormContext()
  const serverError = useContext(ServerErrorsContext)[name]

  useEffect(() => {
    if (serverError) {
      setError(name, { type: 'server', message: serverError })
    }
  }, [serverError, name, setError])

  return <RedwoodController {...props} />
}

export default ServerAwareController

I think this would be nice to have it build it into the framework to able to use with existing FieldError and Label components

irg1008 commented 4 months ago

Works a bit like useErrorStyles hook for custom classes. So much so that the component above can be simplify to just:

import { ComponentProps } from 'react'

import {
  Controller as RedwoodController,
  useErrorStyles,
} from '@redwoodjs/forms'

const ServerAwareController = (
  props: ComponentProps<typeof RedwoodController>
) => {
  useErrorStyles({ name: props.name })
  return <RedwoodController {...props} />
}

export default ServerAwareController

My personal opinion is this would make for a very easy and useful wrapper to rhf controller

dac09 commented 4 months ago

Thank you @irg1008 again for raising an interesting issue, and for the solution here.

I am not a forms expert, but the solution you've suggested here seems quite reasonable. Can you think of a case where you wouldn't want the error styles coming through in a controller?

i.e. Does it make sense to change the export of Controller to be ServerAwareController

dthyresson commented 4 months ago

Hi @irg1008

i.e. Does it make sense to change the export of Controller to be ServerAwareController

am following up here to see what you think of @dac09 proposal or if this scenario could just use some documentation that shows your solution?

irg1008 commented 4 months ago

Yeah I have been pretty busy this week, I will get that moving this weekend np