Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

formStore.setError() with nested values completely broken #3607

Open
ckohen opened this issue Mar 21, 2024 · 2 comments
Open

formStore.setError() with nested values completely broken #3607

ckohen opened this issue Mar 21, 2024 · 2 comments
Labels

Comments

@ckohen
Copy link

ckohen commented Mar 21, 2024

Current behavior

formStore.setError() does not properly set errors to the deepest nested property. It instead sets the error given on the top level property, which prevents FormError from ever having a chance at working.

Steps to reproduce the bug

  1. Open https://stackblitz.com/edit/ariakit-aav7j1?file=src%2FApp.tsx
  2. Click in either empty input field or remove the text in the filled input field
  3. Observe the difference in behavior and the console logs that indicate why that difference occurs

Expected behavior

Properly traverses the nested arrays / objects and sets the requested named error so that FormError displays it.

Workaround

There are a few theoretical workarounds, none of those I have tried yet have worked. The most painful ( and likely to work) workaround I have yet to try is preventing the normal validation flow and manually setting errors with setErrors() since that is a simple replace in core

If you do not use arrays, you can initialize the errors object with the entire object chain, which seems to work.

Possible solutions

Adjust the following lines to always ensure that if there are more rest properties, they get traversed and set to empty objects / arrays as necessary.

function set<T extends FormStoreValues | unknown[]>(
values: T,
path: StringLike | string[],
value: unknown,
): T {
const [k, ...rest] = Array.isArray(path) ? path : `${path}`.split(".");
if (k == null) return values;
const key = k as keyof T;
const isIntegerKey = isInteger(key);
const nextValues = isIntegerKey ? values || [] : values || {};
const nestedValues = nextValues[key];
const result =
rest.length && (Array.isArray(nestedValues) || isObject(nestedValues))
? set(nestedValues, rest, value)
: value;

I'm not sure if there's a reason that this function doesn't always do this, but it may be for reusability when the nested objects / arrays should already exist.

@ckohen
Copy link
Author

ckohen commented Mar 24, 2024

Happy to make a PR for this, just need to know whether to simply remove the Array / Object check or add an extra param to dictate when to skip that check (looks like it needs to be skipped for touched too). I've patched the package with the extra param method for now.

I don't think it would hurt if it set values too, but not 100% sure that won't break anything

@diegohaz
Copy link
Member

We still don't have examples/tests with array fields, so I think creating one with some tests would be a good starting point.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Projects
None yet
Development

No branches or pull requests

2 participants