React-admin: FormDataConsumer: The <ReferenceInput> is not updated with newly fetched data when the `formData` changes and a `filter` exists

Created on 23 Apr 2018  路  14Comments  路  Source: marmelab/react-admin

What you were expecting:
With <FormDataConsumer>, the <ReferenceInput> to be updated with different choices every time formData (or a particularly defined key) is changing, when we use a filter (eg: filter={{cat_id: formData.cat_id}} )

What happened instead:
The choices are not updated, they always show the initially fetched choices

Steps to reproduce:
I use the following filter to get a subset of the referenced data as choices in the ReferenceInput:

filter={{cat_id: formData.cat_id}}

When changing the cat_id (through a different field in the form), the subCat_id field is not re-fetched with the new filtered value.

Related code:

<FormDataConsumer>
        {({formData, ...rest}) =>
          formData.cat_id && (
            <ReferenceInput label="SUBCATEGORY" source="subCat_id" reference="subCategories" allowEmpty
                            perPage={100}
                            filter={{cat_id: formData.cat_id}}
                            sort={{field: 'name', order: 'ASC'}}
                            fullWidth
                            {...rest}
            >
              <SelectInput optionText="name" />
            </ReferenceInput>
          )}
      </FormDataConsumer>

Other information:

Environment

  • React-admin version: Latest master, after a related fix with commit 5ee89998b04b54d7b458845e633de522c41bf162

  • Last version that did not exhibit the issue (if applicable):

  • React version:
  • Browser:
  • Stack trace (in case of a JS error):

Most helpful comment

Yes, the fix was incomplete and has been finalized in https://github.com/marmelab/react-admin/pull/2117 which will be released in 2.2.1. In the mean time, add a key prop to the ReferenceInput component:

<FormDataConsumer>
    {({ formData, ...rest }) =>
        formData.lang && (
        <ReferenceInput
            key={formData.lang}
            label="Category"
            source="category_id"
            reference="categories"
            filter={{ lang: formData.lang }}
            {...rest}
        >
            <SelectInput optionText="title" />
        </ReferenceInput>
        )
    }
</FormDataConsumer>

All 14 comments

I believe that this problem can be solved with 2 actions:

APPROACH 1

  1. We could add a key={formData.cat_id} to the ReferenceInput so that it remounts on every change of the cat_id:
{({formData, ...rest}) =>
          formData.cat_id && (
            <ReferenceInput 
                      key={formData.cat_id}
                      label="SUBCATEGORY"
                     // ...all other props here
            >
              <SelectInput optionText="name" />
            </ReferenceInput>
          )}
  1. Create a PR with a change on the ReferenceInputController so that it always resets the value of the field to null when the component remounts (see last line below):
export class ReferenceInputController extends Component {
    constructor(props) {
        super(props);
        const { perPage, sort, filter } = props;
        // stored as a property rather than state because we don't want redraw of async updates
        this.params = { pagination: { page: 1, perPage }, sort, filter };
        this.debouncedSetFilter = debounce(this.setFilter.bind(this), 500);

        this.props.change(REDUX_FORM_NAME, this.props.source, null, false, false);
    }

UPDATE: This approach fails for editing existing records, as the form value is converted to null. So this approach is a no-go (ignore it), unless there is a better way to handling the edit part.

APPROACH 2

Another approach that I tried and worked was that I explicitly added a "dependsOnValue" prop (eg: .dependsOnValue = {formData.cat_id}). If this prop value changes, then the code in the controller is doing a refetch plus a "set form value to null" action at componentWillReceiveProps.

    componentWillReceiveProps(nextProps) {
        if (
            this.props.record.id !== nextProps.record.id ||
            this.props.dependsOnValue !== nextProps.dependsOnValue
        ) {
            this.fetchReferenceAndOptions(nextProps);
        } else if (this.props.input.value !== nextProps.input.value) {
            this.fetchReference(nextProps);
        }
        if (this.props.dependsOnValue !== nextProps.dependsOnValue) {
            this.props.change(REDUX_FORM_NAME, this.props.source, null, false, false);
        }
    }

What do you think?

If any of the approaches is OK to you, please let me know to submit the respective PR. Thanks.

+1

+1

@fzaninotto This is still a problem (needs a workaround, with extra props), may any change be done in the core? Also may I ask if FormDataConsumer is available for the Filters?

Fixed by #2065

Hi it doesn't seem to be working. For this example, the first value selected in SelectInput lang is kept:

<SimpleForm>
   <SelectInput label="Langue" source="lang" choices={LANG_LIST} />
   <FormDataConsumer>
     {({ formData, ...rest }) => formData.lang &&
       <ReferenceInput label="Rubrique" source="category" reference="postcategories" filter={{ lang: formData.lang }} {...rest}>
         <SelectInput optionText="title" />
       </ReferenceInput>
      }
  </FormDataConsumer>
</SimpleForm>

For this example, the first value selected in SelectInput lang is kept

That's actually a feature :) We always show the selected value

Sorry if i'm not really clear. The only first value is kept. If i change a second time the lang selector, the reference input send api call with in filter value the first value lang.

Can you setup a codesandbox?

The same for me, how can I fix it?

Yes, the fix was incomplete and has been finalized in https://github.com/marmelab/react-admin/pull/2117 which will be released in 2.2.1. In the mean time, add a key prop to the ReferenceInput component:

<FormDataConsumer>
    {({ formData, ...rest }) =>
        formData.lang && (
        <ReferenceInput
            key={formData.lang}
            label="Category"
            source="category_id"
            reference="categories"
            filter={{ lang: formData.lang }}
            {...rest}
        >
            <SelectInput optionText="title" />
        </ReferenceInput>
        )
    }
</FormDataConsumer>

Hello people. This bug is still exist. How fix it ?

Any updates on this issue? Adding a key prop wouldn't help as I am returning a TextInput inside my FormDataConsumer.

Was this page helpful?
0 / 5 - 0 ratings

Related issues

Dragomir-Ivanov picture Dragomir-Ivanov  路  3Comments

marknelissen picture marknelissen  路  3Comments

kdabir picture kdabir  路  3Comments

fzaninotto picture fzaninotto  路  3Comments

phacks picture phacks  路  3Comments