fabian-hiller / valibot

The modular and type safe schema library for validating structural data 🤖
https://valibot.dev
MIT License
5.6k stars 169 forks source link

High CPU usage after upgrading to v0.31.1 #642

Closed seahindeniz closed 3 weeks ago

seahindeniz commented 3 weeks ago

After following the docs and migrating the code, I have noticed that high CPU usage while coding and the auto-completion service was getting stuck on loading state.

Repro case: https://github.com/seahindeniz/valibot-issue

Steps:

  1. Create a new project
    1. SolidStart pnpm create solid
    2. Vanilla pnpm create vite valibot-test --template vanilla
  2. Copy utils folder from the repo mentioned above
  3. Open valibotUtils.ts and try to type something like v.Gen
  4. Observe the high CPU usage

Record:

https://github.com/fabian-hiller/valibot/assets/5789670/2e2f2944-4a1d-48e5-9877-0bcb44bec127

fabian-hiller commented 3 weeks ago

Does this problem still occur when you restart your computer? Are you sure it did not happen with the previous version? Can you double check? I do not think this is related to our source code.

seahindeniz commented 3 weeks ago

Yes, you are right. The odd part is, I was a bit sure that I got this issue after the upgrade but seems like I got the timeline wrong 😓 I have downgraded the versions on the minimal repo, and it is still reproducible.

I have tracked down to this point

export function createForm<TFieldValues extends FieldValues>(props: {
  initialValues?: FormOptions<TFieldValues>['initialValues'];
}) {
  const scope = createFormModular<TFieldValues>({
    initialValues: props.initialValues,
  });

  return scope;
}

If I comment out the return line in this function, there's no CPU issue. But when I return scope from the function, then the TS Server goes crazy.

After converting the function as the same in the type definition file, problem gone. However, if I don't specify the return type manually, it still causes TS server issue, strange.

Final trial

export function createForm<TFieldValues extends FieldValues, TResponseData extends ResponseData = undefined>(options?: FormOptions<TFieldValues>): [
  FormStore<TFieldValues, TResponseData>,
  {
      Form: (props: Omit<FormProps<TFieldValues, TResponseData>, 'of'>) => JSX.Element;
      Field: <TFieldName extends FieldPath<TFieldValues>>(props: FieldPathValue<TFieldValues, TFieldName> extends MaybeValue<string> ? PartialKey<Omit<FieldProps<TFieldValues, TResponseData, TFieldName>, 'of'>, 'type'> : Omit<FieldProps<TFieldValues, TResponseData, TFieldName>, 'of'>) => JSX.Element;
      FieldArray: <TFieldArrayName extends FieldArrayPath<TFieldValues>>(props: Omit<FieldArrayProps<TFieldValues, TResponseData, TFieldArrayName>, 'of'>) => JSX.Element;
  }
] {
  const scope = createFormModular<TFieldValues, TResponseData>(options);

  return scope;
}

The main point of doing this is to create a reusable facilitator function that creates a form but with a bit of sugar to trigger the validate function when the lang/language gets a reactive update.

Since the above version solves the issue, I guess it is safe to close the issue. Thank you for leading it to double-check @fabian-hiller 🙏🏻