Skip to content

Commit

Permalink
Merge pull request #3697 from marmelab/use-many-reference
Browse files Browse the repository at this point in the history
[RFR] Introduce useGetManyReference hook
  • Loading branch information
fzaninotto authored Sep 16, 2019
2 parents 1fe7331 + be69d0e commit 4717447
Show file tree
Hide file tree
Showing 5 changed files with 164 additions and 115 deletions.
1 change: 1 addition & 0 deletions examples/simple/src/posts/PostEdit.js
Original file line number Diff line number Diff line change
Expand Up @@ -155,6 +155,7 @@ const PostEdit = ({ permissions, ...props }) => (
reference="comments"
target="post_id"
addLabel={false}
fullWidth
>
<Datagrid>
<DateField source="created_at" />
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ import assert from 'assert';

import ReferenceManyFieldController from './ReferenceManyFieldController';
import renderWithRedux from '../../util/renderWithRedux';
import { waitForDomChange } from '@testing-library/react';

describe('<ReferenceManyFieldController />', () => {
it('should set loaded to false when related records are not yet fetched', () => {
Expand Down Expand Up @@ -40,13 +41,6 @@ describe('<ReferenceManyFieldController />', () => {
assert.deepEqual(dispatch.mock.calls[0], [
{
meta: {
fetch: 'GET_MANY_REFERENCE',
onFailure: {
notification: {
body: 'ra.notification.http_error',
level: 'warning',
},
},
relatedTo: 'foo_bar@foo_id_undefined',
resource: 'bar',
},
Expand All @@ -55,7 +49,6 @@ describe('<ReferenceManyFieldController />', () => {
id: undefined,
pagination: { page: 1, perPage: 25 },
sort: { field: 'id', order: 'DESC' },
source: 'items',
target: 'foo_id',
},
type: 'RA/CRUD_GET_MANY_REFERENCE',
Expand Down Expand Up @@ -151,7 +144,9 @@ describe('<ReferenceManyFieldController />', () => {
});

it('should support custom source', () => {
const children = jest.fn().mockReturnValue('children');
const children = jest.fn(({ data }) =>
data && data.length > 0 ? data.length : null
);

const { dispatch } = renderWithRedux(
<ReferenceManyFieldController
Expand All @@ -163,19 +158,35 @@ describe('<ReferenceManyFieldController />', () => {
source="customId"
>
{children}
</ReferenceManyFieldController>
</ReferenceManyFieldController>,
{
admin: {
references: {
oneToMany: {
'posts_comments@post_id_1': {
ids: [1],
total: 1,
},
},
},
resources: {
comments: {
data: {
1: {
post_id: 1,
id: 1,
body: 'Hello!',
},
},
},
},
},
}
);

assert.deepEqual(dispatch.mock.calls[0], [
{
meta: {
fetch: 'GET_MANY_REFERENCE',
onFailure: {
notification: {
body: 'ra.notification.http_error',
level: 'warning',
},
},
relatedTo: 'posts_comments@post_id_1',
resource: 'comments',
},
Expand All @@ -184,12 +195,19 @@ describe('<ReferenceManyFieldController />', () => {
id: 1,
pagination: { page: 1, perPage: 25 },
sort: { field: 'id', order: 'DESC' },
source: 'customId',
target: 'post_id',
},
type: 'RA/CRUD_GET_MANY_REFERENCE',
},
]);

expect(children.mock.calls[0][0].data).toEqual({
1: {
post_id: 1,
id: 1,
body: 'Hello!',
},
});
});

it('should call crudGetManyReference when its props changes', () => {
Expand All @@ -208,20 +226,14 @@ describe('<ReferenceManyFieldController />', () => {
);

const { rerender, dispatch } = renderWithRedux(<ControllerWrapper />);
rerender(<ControllerWrapper sort={{ field: 'id', order: 'ASC' }} />);

expect(dispatch).toBeCalledTimes(2);
expect(dispatch).toBeCalledTimes(3); // CRUD_GET_MANY_REFERENCE, CRUD_GET_MANY_REFERENCE_LOADING, FETCH_START
rerender(<ControllerWrapper sort={{ field: 'id', order: 'ASC' }} />);
expect(dispatch).toBeCalledTimes(6);

assert.deepEqual(dispatch.mock.calls[0], [
{
meta: {
fetch: 'GET_MANY_REFERENCE',
onFailure: {
notification: {
body: 'ra.notification.http_error',
level: 'warning',
},
},
relatedTo: 'foo_bar@foo_id_1',
resource: 'bar',
},
Expand All @@ -230,23 +242,15 @@ describe('<ReferenceManyFieldController />', () => {
id: 1,
pagination: { page: 1, perPage: 25 },
sort: { field: 'id', order: 'DESC' },
source: 'id',
target: 'foo_id',
},
type: 'RA/CRUD_GET_MANY_REFERENCE',
},
]);

assert.deepEqual(dispatch.mock.calls[1], [
assert.deepEqual(dispatch.mock.calls[3], [
{
meta: {
fetch: 'GET_MANY_REFERENCE',
onFailure: {
notification: {
body: 'ra.notification.http_error',
level: 'warning',
},
},
relatedTo: 'foo_bar@foo_id_1',
resource: 'bar',
},
Expand All @@ -255,7 +259,6 @@ describe('<ReferenceManyFieldController />', () => {
id: 1,
pagination: { page: 1, perPage: 25 },
sort: { field: 'id', order: 'ASC' },
source: 'id',
target: 'foo_id',
},
type: 'RA/CRUD_GET_MANY_REFERENCE',
Expand Down
Original file line number Diff line number Diff line change
@@ -1,20 +1,14 @@
import { useEffect, useMemo } from 'react';
import { useSelector, useDispatch, shallowEqual } from 'react-redux';
import get from 'lodash/get';

import { crudGetManyReference } from '../../actions';
import {
getIds,
getReferences,
getTotal,
nameRelatedTo,
} from '../../reducer/admin/references/oneToMany';
import { Record, Sort, RecordMap, Identifier } from '../../types';
import { useGetManyReference } from '../../dataProvider';
import { useNotify } from '../../sideEffect';

interface ReferenceManyProps {
data: RecordMap;
ids: Identifier[];
loaded: boolean;
loading: boolean;
referenceBasePath: string;
total: number;
}
Expand Down Expand Up @@ -95,85 +89,36 @@ const useReferenceManyFieldController = ({
sort = { field: 'id', order: 'DESC' },
}: Options): ReferenceManyProps => {
const referenceId = get(record, source);
const relatedTo = useMemo(
() => nameRelatedTo(reference, referenceId, resource, target, filter),
[filter, reference, referenceId, resource, target]
);
const ids = useSelector(selectIds(relatedTo), shallowEqual);
const data = useSelector(selectData(reference, relatedTo), shallowEqual);
const total = useSelector(selectTotal(relatedTo));

const dispatch = useDispatch();

useEffect(
fetchReferences({
reference,
referenceId,
target,
filter,
source,
page,
perPage,
sort,
dispatch,
relatedTo,
}),
[
reference,
referenceId,
resource,
target,
filter,
source,
crudGetManyReference,
page,
perPage,
sort.field,
sort.order,
]
const notify = useNotify();
const { data, ids, total, loading, loaded } = useGetManyReference(
reference,
target,
referenceId,
{ page, perPage },
sort,
filter,
resource,
{
onFailure: error =>
notify(
typeof error === 'string'
? error
: error.message || 'ra.notification.http_error',
'warning'
),
}
);

const referenceBasePath = basePath.replace(resource, reference);

return {
data,
ids,
loaded: typeof ids !== 'undefined',
loaded,
loading,
referenceBasePath,
total,
};
};

const fetchReferences = ({
reference,
referenceId,
target,
filter,
source,
dispatch,
page,
perPage,
sort,
relatedTo,
}) => () => {
dispatch(
crudGetManyReference(
reference,
target,
referenceId,
relatedTo,
{ page, perPage },
sort,
filter,
source
)
);
};

const selectData = (reference, relatedTo) => state =>
getReferences(state, reference, relatedTo);

const selectIds = relatedTo => state => getIds(state, relatedTo);
const selectTotal = relatedTo => state => getTotal(state, relatedTo);

export default useReferenceManyFieldController;
2 changes: 2 additions & 0 deletions packages/ra-core/src/dataProvider/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ import withDataProvider from './withDataProvider';
import useGetOne from './useGetOne';
import useGetList from './useGetList';
import useGetMany from './useGetMany';
import useGetManyReference from './useGetManyReference';
import useUpdate from './useUpdate';
import useUpdateMany from './useUpdateMany';
import useCreate from './useCreate';
Expand All @@ -31,6 +32,7 @@ export {
useGetOne,
useGetList,
useGetMany,
useGetManyReference,
useUpdate,
useUpdateMany,
useCreate,
Expand Down
Loading

0 comments on commit 4717447

Please sign in to comment.