Skip to content

Commit

Permalink
Merge pull request #9386 from marmelab/doc-disabled-input-gotcha
Browse files Browse the repository at this point in the history
[Doc] Add gotcha about `<Input disabled>` usage and `isDirty` form state
  • Loading branch information
adguernier authored Oct 25, 2023
2 parents b2a0488 + 63e4906 commit 255da10
Show file tree
Hide file tree
Showing 7 changed files with 30 additions and 9 deletions.
6 changes: 4 additions & 2 deletions docs/Features.md
Original file line number Diff line number Diff line change
Expand Up @@ -426,6 +426,7 @@ React-admin offers a **rich set of input components and form layouts** to build

For instance, here is how to build a tabbed form for editing a blog post:

{% raw %}
```jsx
import {
TabbedForm,
Expand All @@ -445,7 +446,7 @@ export const PostEdit = () => (
<Edit>
<TabbedForm>
<TabbedForm.Tab label="summary">
<TextInput disabled label="Id" source="id" />
<TextInput label="Id" source="id" InputProps={{ disabled: true }} />
<TextInput source="title" validate={required()} />
<TextInput multiline source="teaser" validate={required()} />
</TabbedForm.Tab>
Expand All @@ -457,7 +458,7 @@ export const PostEdit = () => (
<DateInput label="Publication date" source="published_at" />
<NumberInput source="average_note" validate={[ number(), minValue(0) ]} />
<BooleanInput label="Allow comments?" source="commentable" defaultValue />
<TextInput disabled label="Nb views" source="views" />
<TextInput label="Nb views" source="views" InputProps={{ disabled: true }} />
</TabbedForm.Tab>
<TabbedForm.Tab label="comments">
<ReferenceManyField reference="comments" target="post_id" label={false}>
Expand All @@ -472,6 +473,7 @@ export const PostEdit = () => (
</Edit>
);
```
{% endraw %}

<video controls autoplay playsinline muted loop>
<source src="./img/tabbed-form.webm" type="video/webm"/>
Expand Down
8 changes: 8 additions & 0 deletions docs/Inputs.md
Original file line number Diff line number Diff line change
Expand Up @@ -158,6 +158,14 @@ If `true`, the input is disabled and the user can't change the value.
<TextInput source="title" disabled />
```

**Tip**: The form framework used by react-admin, react-hook-form, [considers](https://github.com/react-hook-form/react-hook-form/pull/10805) that a `disabled` input shouldn't submit any value. So react-hook-form sets the value of all `disabled` inputs to `undefined`. As a consequence, a form with a `disabled` input is always considered `dirty` (i.e. react-hook-form considers that the form values and the initial record values are different), and it triggers [the `warnWhenUnsavedChanges` feature](./EditTutorial.md#warning-about-unsaved-changes) when leaving the form, even though the user changed nothing. The workaround is to set the `disabled` prop on the underlying input component, as follows:

{% raw %}
```jsx
<TextInput source="title" InputProps={{ disabled: true }} />
```
{% endraw %}

## `format`

The `format` prop accepts a callback taking the value from the form state, and returning the input value (which should be a string).
Expand Down
4 changes: 2 additions & 2 deletions docs/Readme.md
Original file line number Diff line number Diff line change
Expand Up @@ -82,13 +82,13 @@ const PostTitle = () => {
export const PostEdit = () => (
<Edit title={<PostTitle />}>
<SimpleForm>
<TextInput disabled source="id" />
<TextInput source="id" InputProps={{ disabled: true }} />
<TextInput source="title" />
<TextInput source="teaser" options={{ multiline: true }} />
<TextInput multiline source="body" />
<DateInput label="Publication date" source="published_at" />
<TextInput source="average_note" />
<TextInput disabled label="Nb views" source="views" />
<TextInput label="Nb views" source="views" InputProps={{ disabled: true }}/>
</SimpleForm>
</Edit>
);
Expand Down
4 changes: 3 additions & 1 deletion docs/Tutorial.md
Original file line number Diff line number Diff line change
Expand Up @@ -703,12 +703,13 @@ export const App = () => (

You can now adjust the `<PostEdit>` component to disable the edition of the primary key (`id`), place it first, and use a textarea for the `body` field, as follows:

{% raw %}
```diff
// in src/posts.tsx
export const PostEdit = () => (
<Edit>
<SimpleForm>
+ <TextInput source="id" disabled />
+ <TextInput source="id" InputProps={{ disabled: true }} />
<ReferenceInput source="userId" reference="users" link="show" />
- <TextInput source="id" />
<TextInput source="title" />
Expand All @@ -718,6 +719,7 @@ export const PostEdit = () => (
</Edit>
);
```
{% endraw %}

If you've understood the `<List>` component, the `<Edit>` component will be no surprise. It's responsible for fetching the record, and displaying the page title. It passes the record down to the `<SimpleForm>` component, which is responsible for the form layout, default values, and validation. Just like `<Datagrid>`, `<SimpleForm>` uses its children to determine the form inputs to display. It expects *input components* as children. `<TextInput>` and `<ReferenceInput>` are such inputs.

Expand Down
6 changes: 5 additions & 1 deletion examples/simple/src/comments/CommentEdit.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -129,7 +129,11 @@ const CommentEdit = props => {
onSubmit={save}
warnWhenUnsavedChanges
>
<TextInput disabled source="id" fullWidth />
<TextInput
source="id"
fullWidth
InputProps={{ disabled: true }}
/>
<ReferenceInput
source="post_id"
reference="posts"
Expand Down
7 changes: 5 additions & 2 deletions examples/simple/src/posts/PostEdit.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -116,7 +116,10 @@ const PostEdit = () => {
justifyContent="space-between"
fullWidth
>
<TextInput disabled source="id" />
<TextInput
InputProps={{ disabled: true }}
source="id"
/>
<TextInput
source="title"
validate={required()}
Expand Down Expand Up @@ -224,7 +227,7 @@ const PostEdit = () => {
validate={[required(), number(), minValue(0)]}
/>
<BooleanInput source="commentable" defaultValue />
<TextInput disabled source="views" />
<TextInput InputProps={{ disabled: true }} source="views" />
<ArrayInput source="pictures">
<SimpleFormIterator>
<TextInput source="url" defaultValue="" />
Expand Down
4 changes: 3 additions & 1 deletion examples/simple/src/users/UserEdit.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -66,7 +66,9 @@ const UserEditForm = () => {
onSubmit={newSave}
>
<TabbedForm.Tab label="user.form.summary" path="">
{permissions === 'admin' && <TextInput disabled source="id" />}
{permissions === 'admin' && (
<TextInput source="id" InputProps={{ disabled: true }} />
)}
<TextInput
source="name"
defaultValue="slim shady"
Expand Down

0 comments on commit 255da10

Please sign in to comment.