diff --git a/HISTORY.md b/HISTORY.md index e0fc4855..41503c53 100644 --- a/HISTORY.md +++ b/HISTORY.md @@ -1,5 +1,8 @@ # History +## 4.0.0-beta.4 +* Removed targets and changed update handlers API. See: [#68](https://github.com/MinimaHQ/re-formality/issues/68) & [#78](https://github.com/MinimaHQ/re-formality/pull/78). + ## 4.0.0-beta.3 * Added `ReactDom` & `ReactNative` targets and changed update handlers API. See: [#68](https://github.com/MinimaHQ/re-formality/issues/68) & [#72](https://github.com/MinimaHQ/re-formality/pull/72). diff --git a/docs/01-Installation.md b/docs/01-Installation.md index 3bbd88b2..8d4dfc35 100644 --- a/docs/01-Installation.md +++ b/docs/01-Installation.md @@ -19,32 +19,6 @@ Under the hood, `re-formality` implemented as PPX. So you need to add it to both ], ``` -## Targets -Library supports 2 targets: -- `ReactDom` -- `ReactNative` - -By default, it's set to `ReactDom`. But you can configure it in quite flexible ways: -1. If you want to apply specific target to all modules in the build, set environment variable `FORMALITY_TARGET` to chosen target: - -```shell -FORMALITY_TARGET=ReactNative bsb -clean-world -make-world -``` - -2. If you want to set specific target on per module basis, do this in your form module: - -```reason -module MyForm = [%form - {target: ReactNative}; - - type input = ...; - type output = ...; - ... -]; -``` - ---- - Before proceeding with actual code, we will elaborate on some core concepts that this library implements. --- diff --git a/docs/04-BasicUsage.md b/docs/04-BasicUsage.md index bfc9f944..43c711e1 100644 --- a/docs/04-BasicUsage.md +++ b/docs/04-BasicUsage.md @@ -174,24 +174,36 @@ Next thing to render is a text input for `email` field: form.blurEmail()} onChange={ - form.updateEmail((~target, input) => { - ...input, - email: target##value, - }) + event => + form.updateEmail( + (input, value) => {...input, email: value}, + event->ReactEvent.Form.target##value, + ) } /> ``` The value of the field is exposed via `form.input` record. For extra safety, we disable all inputs during form submission using `form.submitting` property which is of boolean type. The next 2 functions are very important: -1. `form.blurEmail: ReactEvent.Focus.t => unit`: must be triggered from `onBlur` handler of an input field -2. `form.updateEmail: ((~target: Js.t({..}), input) => input, ReactEvent.Form.t) => unit`: must be triggered from `onChange` handler of an input field. It takes a function as an argument which takes the event target and the current form `input`, must return updated `input` record. Event target, as defined in `reason-react` bindings, is an open object (`Js.t({..})`), which means it is not type-safe but allows to access any target property you need. Most of the time you need either `target##value` or `target##checked`. +1. `form.blurEmail: unit => unit`: must be triggered from `onBlur` handler of an input field +2. `form.updateEmail: ((input, 'inputValue) => input, 'inputValue) => unit`: must be triggered from `onChange` handler of an input field. It takes 2 arguments: + - a function which takes 2 arguments—the current form `input` and updated input value of the current field—and returns updated `input` record + - an updated input value of the current field -For ReactNative users, type signatures would be a bit different since those don't include events related stuff: +The second argument—updated input value—that gets passed to the `form.updateEmail` is exactly the same value as a second argument of the callback. Why it's done this way? Why not just use this value within the callback? It is designed this way to ensure that synthetic DOM event won't be captured by the callback. -1. `form.blurEmail: unit => unit` -2. `form.updateEmail: (input => input) => unit` +```reason +// Bad +onChange={event => { + form.updateEmail(input => { + ...input, + email: event->ReactEvent.Form.target##value, + }); +}} +``` + +As you might already know, [React's `SyntheticEvent` is pooled](https://reactjs.org/docs/events.html#event-pooling). If you would capture the event in the callback (as shown above), since the callback gets triggered asynchronously, by the time it gets called, the event is already null'ed by React and it will result in a runtime error. To avoid this, we ensure that the value is extracted from event outside of the callback. ### Messages To display feedback in UI, we can use `form.emailResult` value. It's exactly what email validator returns but wrapped in `option` type: @@ -325,12 +337,13 @@ let make = () => { form.blurEmail()} onChange={ - form.updateEmail((~target, input) => { - ...input, - email: target##value, - }) + event => + form.updateEmail( + (input, value) => {...input, email: value}, + event->ReactEvent.Form.target##value, + ) } /> {switch (form.emailResult) { @@ -342,12 +355,13 @@ let make = () => { form.blurPassword()} onChange={ - form.updatePassword((~target, input) => { - ...input, - password: target##value, - }) + event => + form.updatePassword( + (input, value) => {...input, password: value}, + event->ReactEvent.Form.target##value, + ) } /> {switch (form.passwordResult) { diff --git a/docs/06-Collections.md b/docs/06-Collections.md index cb2772b5..a542b39a 100644 --- a/docs/06-Collections.md +++ b/docs/06-Collections.md @@ -86,8 +86,8 @@ Getting the input and results, as well as handling addition, removal and field u - `form.addAuthor({name: ""})`: adds `author` entry to collection - `form.removeAuthor(~at: index)`: removes `author` entry from collection -- `form.blurAuthorName(~at: index, ReactEvent.Focus.t)`: triggers blur in `author.name` field at index -- `form.updateAuthorName(~at: index, (~target: Js.t({..}), input) => input, ReactEvent.Form.t)`: updates `author.name` field at index +- `form.blurAuthorName(~at: index)`: triggers blur in `author.name` field at index +- `form.updateAuthorName(~at: index, (input, 'inputValue) => input, 'inputValue)`: updates `author.name` field at index - `form.authorNameResult(~at=index)`: returns validation result for `author.name` field at index - `form.authorsResult`: returns result of the whole collection validation, if validator exists @@ -104,22 +104,25 @@ let form = MyForm.useForm(...); form.blurAuthorName(~at=index)} onChange={ - form.updateAuthorName(~at=index, (~target, input) => - { - ...input, - authors: - input.authors - ->Array.mapWithIndex((idx, author) => - if (idx != index) { - author; - } else { - {name: target##value}; - } - ), - } - ) + event => + form.updateAuthorName( + ~at=index, + (input, value) => { + ...input, + authors: + input.authors + ->Array.mapWithIndex((idx, author) => + if (idx != index) { + author; + } else { + {name: value}; + } + ), + }, + event->ReactEvent.Form.target##value, + ) } />