Skip to content

Commit

Permalink
Merge pull request #4042 from marmelab/fix-autocompletearrayinput-emp…
Browse files Browse the repository at this point in the history
…ty-items

Fix AutocompleteArrayInput inside ReferenceArrayInput
  • Loading branch information
fzaninotto committed Nov 26, 2019
2 parents f0757ce + 8dc6813 commit f083e44
Show file tree
Hide file tree
Showing 3 changed files with 34 additions and 18 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -564,7 +564,14 @@ describe('<ReferenceArrayInputController />', () => {
input={{ value: [5] }}
>
{children}
</ReferenceArrayInputController>
</ReferenceArrayInputController>,
{
admin: {
resources: { tags: { data: {}, list: {} } },
references: { possibleValues: {} },
ui: { viewVersion: 1 },
},
}
);

await wait();
Expand Down
Original file line number Diff line number Diff line change
@@ -1,7 +1,8 @@
import { useMemo, useState, useEffect, useRef } from 'react';
import { useSelector } from 'react-redux';
import isEqual from 'lodash/isEqual';
import difference from 'lodash/difference';
import { Pagination, Record, Sort } from '../../types';
import { Pagination, Record, Sort, ReduxState } from '../../types';
import { useGetMany } from '../../dataProvider';
import { FieldInputProps } from 'react-final-form';
import useGetMatching from '../../dataProvider/useGetMatching';
Expand Down Expand Up @@ -49,12 +50,18 @@ const useReferenceArrayInputController = ({
// only the missing references when the input value changes
const inputValue = useRef(input.value);
const [idsToFetch, setIdsToFetch] = useState(input.value);
const [idsToGetFromStore, setIdsToGetFromStore] = useState([]);
const referenceRecordsFromStore = useSelector((state: ReduxState) =>
idsToGetFromStore.map(id => state.admin.resources[reference].data[id])
);

// optimization: we fetch selected items only once. When the user selects more items,
// as we already have the past selected items in the store, we don't fetch them.
useEffect(() => {
const newIdsToFetch = difference(input.value, inputValue.current);

if (newIdsToFetch.length > 0) {
setIdsToFetch(newIdsToFetch);
setIdsToGetFromStore(inputValue.current);
}
inputValue.current = input.value;
}, [input.value, setIdsToFetch]);
Expand Down Expand Up @@ -90,11 +97,18 @@ const useReferenceArrayInputController = ({
[defaultFilter, filter, filterToQuery]
);

const { data: referenceRecords, loaded } = useGetMany(
const { data: referenceRecordsFetched, loaded } = useGetMany(
reference,
idsToFetch || []
);

const referenceRecords = referenceRecordsFetched
? referenceRecordsFetched.concat(referenceRecordsFromStore)
: referenceRecordsFromStore;

// filter out not found references - happens when the dataProvider doesn't guarantee referential integrity
const finalReferenceRecords = referenceRecords.filter(Boolean);

const { data: matchingReferences } = useGetMatching(
reference,
pagination,
Expand All @@ -105,11 +119,6 @@ const useReferenceArrayInputController = ({
options
);

// filter out not found references - happens when the dataProvider doesn't guarantee referential integrity
const finalReferenceRecords = referenceRecords
? referenceRecords.filter(Boolean)
: [];

// We merge the currently selected records with the matching ones, otherwise
// the component displaying the currently selected records may fail
const finalMatchingReferences =
Expand Down
18 changes: 9 additions & 9 deletions packages/ra-core/src/dataProvider/defaultDataProvider.ts
Original file line number Diff line number Diff line change
@@ -1,11 +1,11 @@
export default {
create: () => Promise.resolve(null), // avoids adding a context in tests
delete: () => Promise.resolve(null), // avoids adding a context in tests
deleteMany: () => Promise.resolve(null), // avoids adding a context in tests
getList: () => Promise.resolve(null), // avoids adding a context in tests
getMany: () => Promise.resolve(null), // avoids adding a context in tests
getManyReference: () => Promise.resolve(null), // avoids adding a context in tests
getOne: () => Promise.resolve(null), // avoids adding a context in tests
update: () => Promise.resolve(null), // avoids adding a context in tests
updateMany: () => Promise.resolve(null), // avoids adding a context in tests
create: () => Promise.resolve({ data: null }), // avoids adding a context in tests
delete: () => Promise.resolve({ data: null }), // avoids adding a context in tests
deleteMany: () => Promise.resolve({ data: [] }), // avoids adding a context in tests
getList: () => Promise.resolve({ data: [], total: 0 }), // avoids adding a context in tests
getMany: () => Promise.resolve({ data: [] }), // avoids adding a context in tests
getManyReference: () => Promise.resolve({ data: [], total: 0 }), // avoids adding a context in tests
getOne: () => Promise.resolve({ data: null }), // avoids adding a context in tests
update: () => Promise.resolve({ data: null }), // avoids adding a context in tests
updateMany: () => Promise.resolve({ data: [] }), // avoids adding a context in tests
};

0 comments on commit f083e44

Please sign in to comment.