From ead18a6f0fc271e16531a3f20558345832f04572 Mon Sep 17 00:00:00 2001 From: Volodymyr Zakhovaiko Date: Fri, 17 Jan 2025 15:30:59 +0100 Subject: [PATCH 1/4] Added faq to docs --- packages/uniforms/src/useForm.ts | 2 +- website/docs/getting-started/faq.mdx | 308 +++++++++++++++++++++++++++ 2 files changed, 309 insertions(+), 1 deletion(-) create mode 100644 website/docs/getting-started/faq.mdx diff --git a/packages/uniforms/src/useForm.ts b/packages/uniforms/src/useForm.ts index a810e270d..46d1c7df2 100644 --- a/packages/uniforms/src/useForm.ts +++ b/packages/uniforms/src/useForm.ts @@ -14,7 +14,7 @@ Two most common reasons for this error are: 1. Component calling this function doesn't have a parent Form component in the tree. 2. A duplicate uniforms dependency is installed in node_modules. -For more info check FAQ: https://uniforms.tools/docs/faq/#useform-must-be-used-within-a-form +For more info check FAQ: https://uniforms.tools/docs/getting-started/faq/#useform-must-be-used-within-a-form `, ); return context; diff --git a/website/docs/getting-started/faq.mdx b/website/docs/getting-started/faq.mdx new file mode 100644 index 000000000..7b075ccf1 --- /dev/null +++ b/website/docs/getting-started/faq.mdx @@ -0,0 +1,308 @@ +--- +id: faq +title: FAQ +position: 4 +--- + +### How can I customize/style my form fields? + +You can style your form fields simply by passing a `className` property. + +### How can I create custom fields? + +You can create a custom field by wrapping your component inside the [`connectField`](/docs/api-helpers#connectfieldcomponent-options). + +The `connectField` will pass various props related to the form management, such as `onChange()` function, current field's value, errors and so on, to your component. + +Please visit the [Tutorials > Creating a custom field](/docs/tutorials-creating-custom-field) tutorial to see how to create your own fields. + +### How can I use a custom field in my form? + +You can tell your schema to use your custom field by adding the `uniforms` property. + +Example in JSONSchema: + +```tsx +const schema = { + /*...*/ + firstName: { + type: 'string', + uniforms: MyCustomFirstNameField, + }, + /*...*/ +}; +``` + +We say that the component used for the `firstName` property will be the `MyCustomFirstNameField`. + +You can also leave the schema untouched and pass your custom field directly to the `AutoField` in a `component` property instead: + +```tsx + + /*...*/ + + /*...*/ + +``` + +### How can I pass additional props to the custom field? + +You can pass any additional props to your custom field, by converting the `uniforms` property to the type of object, with the `component` key. Any other keys will be treated as props. + +E.g. in JSONSchema: + +```tsx +const schema = { + /*...*/ + firstName: { + type: 'string', + uniforms: { + component: MyCustomFirstNameField, + propA: 1, + propB: 2, + }, + }, + /*...*/ +}; +``` + +We say that the component used for the `firstName` property will be the `MyCustomFirstNameField` and it will receive 2 additonal props: `propA` and `propB`. + +You can also leave the schema untouched and pass your custom field with props directly to the `AutoField` instead: + +```tsx + + /*...*/ + + /*...*/ + +``` + +### How can I have a dynamic label? (e.g. handling i18n) + +There are few ways to handle that, depending on the level of abstraction you want to do it - schema, field or `AutoField` component. + +On the **schema** level, you can use `uniforms: {...}` object property to pass extra props to the field. +A function returning it (`uniforms: () => ({...})`) is also accepted. With it, dynamic labels can be fetched from any source. + +On the **field** level, you can prepare your own component set, where you will use `{label}` instead of `{label}`. + +While the first one is schema-dependent and the second is theme-dependent, there's an additional option, somewhere in between. You can create a custom `AutoField` component, based on a builtin one, where you provide some additional props and label might be one of them (based on other props, like name or some schema field). + +### How can I change the way my form validates? + +Any form can be validated in one of those three styles: + +- `onChange` + Validate on every change. + +- `onChangeAfterSubmit` _(default)_ + Validate on every change, but only after first submit. + +- `onSubmit` + Validate on every submit. + +You change the way your form validates by setting `validate` prop: + +```tsx + +``` + +**Note:** If your schema validator accepts any options, those can be passed in `validator` prop: + +```tsx + +``` + +### How can I reset my form state? + +You can use [React `ref` prop](https://facebook.github.io/react/docs/more-about-refs.html) or [`formRef`](/docs/api-context-data#formref) to manually access form methods. + +These methods are: + +- `change(key, value)` +- `reset()` +- `submit()` +- `validate()` _(added in `ValidatedForm`)_ + +```tsx +import { useRef } from 'react'; + +const MyForm = ({ schema, onSubmit }) => { + const formRef = useRef(); + + return ( +
+ + formRef.reset()}>Reset + formRef.submit()}>Submit +
+ ); +}; +``` + +or the hook way: + +```tsx +function FormControls() { + const { formRef } = useForm(); + + return ( + <> + + + + ); +} + +function App() { + return ( + + + + ); +} +``` + +You can find more about form methods [here](/docs/api-forms). + +### I want my form to be prefilled with data. How can I do that? + +You can pass the initial data to the form by using the `model` prop. + +The `model` is an object with `{field: value}` structure. It doesn't matter if it has a prototype or not, but keep in mind that in `onSubmit` or in `onChangeModel` you'll receive a plain object. If you treat form as an input, then this is a value. + +### How can I transform my model? + +You should `modelTransform`. It is a function transforming one model into another. It's used in a few situations (called 'modes') described below. + +**Remember not to mutate a given model!** + +```tsx +function transform(mode, model) { + // This model will be passed to the fields. + if (mode === 'form') { + /* ... */ + } + + // This model will be submitted. + if (mode === 'submit') { + /* ... */ + } + + // This model will be validated. + if (mode === 'validate') { + /* ... */ + } + + // Otherwise, return unaltered model. + return model; +} + +; +``` + +### How can I make my form autofocused? + +You can take a reference to the field and manually trigger `.focus()`: + +```tsx +import { useRef } from 'react'; + +const inputRef = useRef(); + +; +``` + +### How can I create a multi-step form? + +What is a multi-step form? Well, one can imagine at least two completely separate definitions: + +1. **A set of independent forms with a shared state.** That's the _easier_ one as it's always possible. Each step renders a separate form, with a different schema/validator/style and moves to the next one when submitted, accumulating submitted data. + + This handles not only multi-step forms but also forms wizards with a tree-like structure (i.e. next step bases on the answers). Optional steps (_skip step 2 if age < 40_) and contextual validation (_field Y in step 2 has to be greater than the value of X in step 1_) is also possible. + + But it gets even better - each step may use a different forms library! It makes no sense but is definitely possible - each form is independent, and the orchestration happens in the application. + +2. **A single form displayed in parts.** It is, of course, possible to implement it, but the number of all configurations and options is _massive_. But let's skip that and see where a bigger problem is: the validation. In **1.** each step is validated separately (i.e. can have a separate schema). Here, we have only one schema, and the schema itself has to know that _some_ fields were not yet visible. + + Let's make an example. The schema is very basic: `{ a: string, b: string }` (TypeScript notation). Now, as both `a` and `b` are required, a _valid_ model has to have both. If the first step will render only the `a` field (`b` is on the next page), it's impossible to validate the form. This leads to a situation where the schema (logic) depends on the steps (UI). On the other hand, the form could be validated only at the end. The UX of this solution is terrible though - imagine a _there's an error ten pages back_ error! + +We are not planning to provide any out-of-the-box support for multi-step forms as option **1.** is most of the time the best. It's not only the cleanest but also less complicated as well as doesn't rely on any library. + +### How can I know a current form state? + +A current form state is available in [React context](https://reactjs.org/docs/context.html), accessible through `useForm` and `useField(name)` hooks. + +The context data consists of various properties which can be found in [here](/docs/api-context-data). + +##### Example usage: + +```tsx +function SubmittingState() { + const uniforms = useForm(); + return uniforms.submitting ? 'Submitting...' : null; +} + + + +; +``` + +### I want to disable a submit button until there is a difference between the current form state and my model. How can I do it? + +Basically, you have to find out whether there is a difference between a current form state and your model, e.g. by calling lodash's `isEqual` function. +Current form state can be accessed through the context (see [How can I know a current form state?](/docs/getting-started/faq#how-can-i-know-a-current-form-state)) and form model can be passed as an ordinary prop: + +```tsx +function DifferentSubmitField({ initialModel }) { + const { model } = useForm(); + return ; +} + +const ChangedForm = ({ model }) => ( + + + +); +``` + +### Why am I suddenly getting type errors in my form components? + +After introduction of TypeScript in `uniforms@3.0.0`, in the initial versions all form components in theme packages were typed as `any`. +Natural strict typing is not possible due to TypeScript constraints. In one of the versions we have decided to change this approach and explicitly cast all of the form types. +If you experience any errors regarding form types, please [file us a bug report](https://github.com/vazco/uniforms/issues/new?assignees=&labels=&template=bug-report.md) and use one of the following workarounds for the time being in your project. + +```tsx +const AnyAutoForm: any = AutoForm; +; + +// or + +const anyProps: any = { + untypedProp: 1 +} + +``` + +### "`useForm` must be used within a form" + +uniforms uses a `React.Context` in order to keep the state of the whole form. +The provider for this context is rendered by `BaseForm`, and in turn all the other form components inheriting it. + +There are two most common issues causing this problem: + +1. **The component calling this function does not have a Form component above it anywhere in the component tree.** + + To fix this, wrap this component within a parent Form component (does not have to be direct). + +2. **There are multiple versions of `uniforms` installed in your `node_modules`**. + + This usually happens when you have installed more than one version of the core `uniforms` package. It can happen when you have a mismatch of versions between any of your `uniforms` related dependencies. + + Ensure all your uniforms packages versions, clean any `node_modules` directories and reinstall dependencies to resolve this error. From ce9580d9d9b05b2cde368d58a2be1ad1fae64229 Mon Sep 17 00:00:00 2001 From: Volodymyr Zakhovaiko Date: Fri, 17 Jan 2025 15:51:58 +0100 Subject: [PATCH 2/4] Added useField opt --- website/docs/getting-started/faq.mdx | 14 ++++++++------ 1 file changed, 8 insertions(+), 6 deletions(-) diff --git a/website/docs/getting-started/faq.mdx b/website/docs/getting-started/faq.mdx index 7b075ccf1..e83394c15 100644 --- a/website/docs/getting-started/faq.mdx +++ b/website/docs/getting-started/faq.mdx @@ -1,7 +1,7 @@ --- id: faq title: FAQ -position: 4 +sidebar_position: 4 --- ### How can I customize/style my form fields? @@ -10,11 +10,13 @@ You can style your form fields simply by passing a `className` property. ### How can I create custom fields? -You can create a custom field by wrapping your component inside the [`connectField`](/docs/api-helpers#connectfieldcomponent-options). +You can create a custom field by wrapping your component inside the [`connectField`](/docs/api-reference/helpers#connect-field). The `connectField` will pass various props related to the form management, such as `onChange()` function, current field's value, errors and so on, to your component. -Please visit the [Tutorials > Creating a custom field](/docs/tutorials-creating-custom-field) tutorial to see how to create your own fields. +Or you can use a hook version of `connectField` called [`useField`](/docs/api-reference/helpers#usefield). It takes the field name, field props, and optional options as arguments, returning field-specific props and context. + +Please visit the [Examples > Custom field](/docs/examples/custom-field) example to see how to create your own fields. ### How can I use a custom field in my form? @@ -121,7 +123,7 @@ You change the way your form validates by setting `validate` prop: ### How can I reset my form state? -You can use [React `ref` prop](https://facebook.github.io/react/docs/more-about-refs.html) or [`formRef`](/docs/api-context-data#formref) to manually access form methods. +You can use [React `ref` prop](https://facebook.github.io/react/docs/more-about-refs.html) or [`formRef`](/docs/api-reference/context-data#formref) to manually access form methods. These methods are: @@ -169,7 +171,7 @@ function App() { } ``` -You can find more about form methods [here](/docs/api-forms). +You can find more about form methods [here](/docs/api-reference/forms). ### I want my form to be prefilled with data. How can I do that? @@ -239,7 +241,7 @@ We are not planning to provide any out-of-the-box support for multi-step forms a A current form state is available in [React context](https://reactjs.org/docs/context.html), accessible through `useForm` and `useField(name)` hooks. -The context data consists of various properties which can be found in [here](/docs/api-context-data). +The context data consists of various properties which can be found in [here](/docs/api-reference/context-data). ##### Example usage: From a0f80ccc38a3f13f2c51f405a10f48eb3eb62cce Mon Sep 17 00:00:00 2001 From: Volodymyr Zakhovaiko Date: Fri, 17 Jan 2025 15:59:52 +0100 Subject: [PATCH 3/4] Added a few fixes --- website/docs/getting-started/faq.mdx | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/website/docs/getting-started/faq.mdx b/website/docs/getting-started/faq.mdx index e83394c15..53683df0a 100644 --- a/website/docs/getting-started/faq.mdx +++ b/website/docs/getting-started/faq.mdx @@ -18,7 +18,9 @@ Or you can use a hook version of `connectField` called [`useField`](/docs/api-re Please visit the [Examples > Custom field](/docs/examples/custom-field) example to see how to create your own fields. -### How can I use a custom field in my form? +----------------- + +**How can I use a custom field in my form?** You can tell your schema to use your custom field by adding the `uniforms` property. @@ -237,9 +239,11 @@ What is a multi-step form? Well, one can imagine at least two completely separat We are not planning to provide any out-of-the-box support for multi-step forms as option **1.** is most of the time the best. It's not only the cleanest but also less complicated as well as doesn't rely on any library. +Check out our multi-step form example [here](/docs/examples/multi-step-form). + ### How can I know a current form state? -A current form state is available in [React context](https://reactjs.org/docs/context.html), accessible through `useForm` and `useField(name)` hooks. +A current form state is available in [React context](https://react.dev/reference/react/useContext), accessible through `useForm` and `useField(name)` hooks. The context data consists of various properties which can be found in [here](/docs/api-reference/context-data). From 512d66d80e473b13ad191259cadaa70f6625c7e8 Mon Sep 17 00:00:00 2001 From: Volodymyr Zakhovaiko Date: Fri, 17 Jan 2025 16:00:10 +0100 Subject: [PATCH 4/4] Changed order --- website/docs/getting-started/faq.mdx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/website/docs/getting-started/faq.mdx b/website/docs/getting-started/faq.mdx index 53683df0a..47229b844 100644 --- a/website/docs/getting-started/faq.mdx +++ b/website/docs/getting-started/faq.mdx @@ -1,7 +1,7 @@ --- id: faq title: FAQ -sidebar_position: 4 +sidebar_position: 5 --- ### How can I customize/style my form fields?