From 2487df1bd26f012a239f1455787659af090a9713 Mon Sep 17 00:00:00 2001 From: thiery Date: Sun, 2 Jun 2019 11:45:09 +0200 Subject: [PATCH 1/6] convert ReferenceArrayFieldController to a functional component --- .../field/ReferenceArrayFieldController.tsx | 69 ++++++++----------- 1 file changed, 28 insertions(+), 41 deletions(-) diff --git a/packages/ra-core/src/controller/field/ReferenceArrayFieldController.tsx b/packages/ra-core/src/controller/field/ReferenceArrayFieldController.tsx index a591e8e24c..9f383fefff 100644 --- a/packages/ra-core/src/controller/field/ReferenceArrayFieldController.tsx +++ b/packages/ra-core/src/controller/field/ReferenceArrayFieldController.tsx @@ -1,4 +1,4 @@ -import { Component, ReactNode } from 'react'; +import { FunctionComponent, ReactNode, useEffect, ReactElement } from 'react'; import { connect } from 'react-redux'; import get from 'lodash/get'; @@ -65,49 +65,36 @@ interface Props { * * */ -export class UnconnectedReferenceArrayFieldController extends Component { - componentDidMount() { - this.fetchReferences(); - } - - componentWillReceiveProps(nextProps) { - if ( - (this.props.record || { id: undefined }).id !== - (nextProps.record || {}).id - ) { - this.fetchReferences(nextProps); - } - } - - fetchReferences({ crudGetManyAccumulate, reference, ids } = this.props) { +export const UnconnectedReferenceArrayFieldController: FunctionComponent< + Props +> = ({ + resource, + reference, + data, + ids, + children, + basePath, + crudGetManyAccumulate, + record, +}) => { + useEffect(() => { crudGetManyAccumulate(reference, ids); - } + }, [reference, ids, crudGetManyAccumulate, record.id]); - render() { - const { - resource, - reference, - data, - ids, - children, - basePath, - } = this.props; + const referenceBasePath = basePath.replace(resource, reference); // FIXME obviously very weak - const referenceBasePath = basePath.replace(resource, reference); // FIXME obviously very weak - - return children({ - // tslint:disable-next-line:triple-equals - loadedOnce: data != undefined, - ids, - data, - referenceBasePath, - currentSort: { - field: 'id', - order: 'ASC', - }, - }); - } -} + return children({ + // tslint:disable-next-line:triple-equals + loadedOnce: data != undefined, + ids, + data, + referenceBasePath, + currentSort: { + field: 'id', + order: 'ASC', + }, + }) as ReactElement; +}; const mapStateToProps = (state: ReduxState, props: Props) => { const { record, source, reference } = props; From 2cf5659e19f2122ee8d26cac116fcc9fbc0bdd30 Mon Sep 17 00:00:00 2001 From: thiery Date: Sun, 2 Jun 2019 11:58:31 +0200 Subject: [PATCH 2/6] use hook to connect ReferenceArrayFieldController --- .../field/ReferenceArrayFieldController.tsx | 44 +++++++------------ 1 file changed, 16 insertions(+), 28 deletions(-) diff --git a/packages/ra-core/src/controller/field/ReferenceArrayFieldController.tsx b/packages/ra-core/src/controller/field/ReferenceArrayFieldController.tsx index 9f383fefff..ace0bd6e1f 100644 --- a/packages/ra-core/src/controller/field/ReferenceArrayFieldController.tsx +++ b/packages/ra-core/src/controller/field/ReferenceArrayFieldController.tsx @@ -1,17 +1,11 @@ import { FunctionComponent, ReactNode, useEffect, ReactElement } from 'react'; -import { connect } from 'react-redux'; +// @ts-ignore +import { useDispatch, useSelector } from 'react-redux'; import get from 'lodash/get'; -import { crudGetManyAccumulate as crudGetManyAccumulateAction } from '../../actions'; +import { crudGetManyAccumulate } from '../../actions'; import { getReferencesByIds } from '../../reducer/admin/references/oneToMany'; -import { - ReduxState, - Record, - RecordMap, - Dispatch, - Sort, - Identifier, -} from '../../types'; +import { ReduxState, Record, RecordMap, Sort, Identifier } from '../../types'; interface ChildrenFuncParams { loadedOnce: boolean; @@ -24,7 +18,6 @@ interface ChildrenFuncParams { interface Props { basePath: string; children: (params: ChildrenFuncParams) => ReactNode; - crudGetManyAccumulate: Dispatch; data?: RecordMap; ids: Identifier[]; record?: Record; @@ -65,21 +58,22 @@ interface Props { * * */ -export const UnconnectedReferenceArrayFieldController: FunctionComponent< - Props -> = ({ +export const ReferenceArrayFieldController: FunctionComponent = ({ resource, reference, - data, - ids, children, basePath, - crudGetManyAccumulate, record, + source, }) => { + const dispatch = useDispatch(); + const { data, ids } = useSelector( + getReferenceArray({ record, source, reference }), + [record, source, reference] + ); useEffect(() => { - crudGetManyAccumulate(reference, ids); - }, [reference, ids, crudGetManyAccumulate, record.id]); + dispatch(crudGetManyAccumulate(reference, ids)); + }, [reference, ids, record.id]); const referenceBasePath = basePath.replace(resource, reference); // FIXME obviously very weak @@ -96,8 +90,9 @@ export const UnconnectedReferenceArrayFieldController: FunctionComponent< }) as ReactElement; }; -const mapStateToProps = (state: ReduxState, props: Props) => { - const { record, source, reference } = props; +const getReferenceArray = ({ record, source, reference }) => ( + state: ReduxState +) => { const ids = get(record, source) || []; return { data: getReferencesByIds(state, reference, ids), @@ -105,11 +100,4 @@ const mapStateToProps = (state: ReduxState, props: Props) => { }; }; -const ReferenceArrayFieldController = connect( - mapStateToProps, - { - crudGetManyAccumulate: crudGetManyAccumulateAction, - } -)(UnconnectedReferenceArrayFieldController); - export default ReferenceArrayFieldController; From 23f482837074d234b08d5bd06fd0a3c528815663 Mon Sep 17 00:00:00 2001 From: thiery Date: Sun, 2 Jun 2019 13:06:03 +0200 Subject: [PATCH 3/6] add useReferenceArray hooks --- .../field/ReferenceArrayFieldController.tsx | 51 ++-------- .../src/controller/field/useReferenceArray.ts | 99 +++++++++++++++++++ 2 files changed, 109 insertions(+), 41 deletions(-) create mode 100644 packages/ra-core/src/controller/field/useReferenceArray.ts diff --git a/packages/ra-core/src/controller/field/ReferenceArrayFieldController.tsx b/packages/ra-core/src/controller/field/ReferenceArrayFieldController.tsx index ace0bd6e1f..546c5aa9c9 100644 --- a/packages/ra-core/src/controller/field/ReferenceArrayFieldController.tsx +++ b/packages/ra-core/src/controller/field/ReferenceArrayFieldController.tsx @@ -1,11 +1,6 @@ import { FunctionComponent, ReactNode, useEffect, ReactElement } from 'react'; -// @ts-ignore -import { useDispatch, useSelector } from 'react-redux'; -import get from 'lodash/get'; -import { crudGetManyAccumulate } from '../../actions'; -import { getReferencesByIds } from '../../reducer/admin/references/oneToMany'; -import { ReduxState, Record, RecordMap, Sort, Identifier } from '../../types'; +import useReferenceArray from './useReferenceArray' interface ChildrenFuncParams { loadedOnce: boolean; @@ -58,46 +53,20 @@ interface Props { * * */ -export const ReferenceArrayFieldController: FunctionComponent = ({ +const ReferenceArrayFieldController: FunctionComponent = ({ resource, reference, - children, basePath, record, source, }) => { - const dispatch = useDispatch(); - const { data, ids } = useSelector( - getReferenceArray({ record, source, reference }), - [record, source, reference] - ); - useEffect(() => { - dispatch(crudGetManyAccumulate(reference, ids)); - }, [reference, ids, record.id]); - - const referenceBasePath = basePath.replace(resource, reference); // FIXME obviously very weak - - return children({ - // tslint:disable-next-line:triple-equals - loadedOnce: data != undefined, - ids, - data, - referenceBasePath, - currentSort: { - field: 'id', - order: 'ASC', - }, - }) as ReactElement; -}; - -const getReferenceArray = ({ record, source, reference }) => ( - state: ReduxState -) => { - const ids = get(record, source) || []; - return { - data: getReferencesByIds(state, reference, ids), - ids, - }; + return children(useReferenceArray({ + resource, + reference, + basePath, + record, + source, + })) as ReactElement; }; -export default ReferenceArrayFieldController; +export default ReferenceArrayFieldController; \ No newline at end of file diff --git a/packages/ra-core/src/controller/field/useReferenceArray.ts b/packages/ra-core/src/controller/field/useReferenceArray.ts new file mode 100644 index 0000000000..9f369c3f31 --- /dev/null +++ b/packages/ra-core/src/controller/field/useReferenceArray.ts @@ -0,0 +1,99 @@ +import { FunctionComponent, ReactNode, useEffect, ReactElement } from 'react'; +// @ts-ignore +import { useDispatch, useSelector } from 'react-redux'; +import get from 'lodash/get'; + +import { crudGetManyAccumulate } from '../../actions'; +import { getReferencesByIds } from '../../reducer/admin/references/oneToMany'; +import { ReduxState, Record, RecordMap, Sort, Identifier } from '../../types'; + +interface ReferenceArrayProps { + loadedOnce: boolean; + ids: Identifier[]; + data: RecordMap; + referenceBasePath: string; + currentSort: Sort; +} + +interface Option { + basePath: string; + record?: Record; + reference: string; + resource: string; + source: string; +} + +/** + * A container component that fetches records from another resource specified + * by an array of *ids* in current record. + * + * You must define the fields to be passed to the iterator component as children. + * + * @example Display all the products of the current order as datagrid + * // order = { + * // id: 123, + * // product_ids: [456, 457, 458], + * // } + * + * + * + * + * + * + * + * + * + * @example Display all the categories of the current product as a list of chips + * // product = { + * // id: 456, + * // category_ids: [11, 22, 33], + * // } + * + * + * + * + * + * + */ +const useReferenceArray = ({ + resource, + reference, + basePath, + record, + source, +}: Option): ReferenceArrayProps => { + const dispatch = useDispatch(); + const { data, ids } = useSelector( + getReferenceArray({ record, source, reference }), + [record, source, reference] + ); + useEffect(() => { + dispatch(crudGetManyAccumulate(reference, ids)); + }, [reference, ids, record.id]); + + const referenceBasePath = basePath.replace(resource, reference); // FIXME obviously very weak + + return { + // tslint:disable-next-line:triple-equals + loadedOnce: data != undefined, + ids, + data, + referenceBasePath, + currentSort: { + field: 'id', + order: 'ASC', + }, + }; +}; + +const getReferenceArray = ({ record, source, reference }) => ( + state: ReduxState +) => { + const ids = get(record, source) || []; + return { + data: getReferencesByIds(state, reference, ids), + ids, + }; +}; + +export default useReferenceArray; From eb15b3b2dd5969a2e21aaa04bc5ab73ac1f80e4d Mon Sep 17 00:00:00 2001 From: thiery Date: Sun, 2 Jun 2019 20:15:10 +0200 Subject: [PATCH 4/6] update test --- .../ReferenceArrayFieldController.spec.tsx | 221 +++++++++++------- .../field/ReferenceArrayFieldController.tsx | 3 +- 2 files changed, 141 insertions(+), 83 deletions(-) diff --git a/packages/ra-core/src/controller/field/ReferenceArrayFieldController.spec.tsx b/packages/ra-core/src/controller/field/ReferenceArrayFieldController.spec.tsx index 8f668082ba..d114d4ec80 100644 --- a/packages/ra-core/src/controller/field/ReferenceArrayFieldController.spec.tsx +++ b/packages/ra-core/src/controller/field/ReferenceArrayFieldController.spec.tsx @@ -1,124 +1,183 @@ import React from 'react'; -import assert from 'assert'; -import { shallow } from 'enzyme'; -import { UnconnectedReferenceArrayFieldController as ReferenceArrayFieldController } from './ReferenceArrayFieldController'; +import { cleanup } from 'react-testing-library'; -describe('', () => { - const crudGetManyAccumulate = jest.fn(); +import ReferenceArrayFieldController from './ReferenceArrayFieldController'; +import renderWithRedux from '../../util/renderWithRedux'; +import { crudGetManyAccumulate } from '../../actions'; - it('should set the loadedOnce prop to false when related records are not yet fetched', () => { - const children = jest.fn(); +describe('', () => { + afterEach(cleanup); + // it('should set the loadedOnce prop to false when related records are not yet fetched', () => { + // const children = jest.fn().mockReturnValue('child'); - shallow( - - {children} - - ); - assert.equal(children.mock.calls[0][0].loadedOnce, false); - }); + // renderWithRedux( + // + // {children} + // , + // { + // admin: { + // resources: { + // bar: { + // data: {}, + // }, + // }, + // }, + // } + // ); + // expect(children.mock.calls[0][0]).toEqual({ + // currentSort: { field: 'id', order: 'ASC' }, + // loadedOnce: false, + // referenceBasePath: '', + // data: null, + // ids: [1, 2], + // }); + // }); - it('should set the loadedOnce prop to true when at least one related record is found', () => { - const children = jest.fn(); + // it('should set the loadedOnce prop to true when at least one related record is found', () => { + // const children = jest.fn().mockReturnValue('child'); - shallow( - - {children} - - ); + // renderWithRedux( + // + // {children} + // , + // { + // admin: { + // resources: { + // bar: { + // data: { + // 2: { + // id: 2, + // value: 'reference2', + // }, + // }, + // }, + // }, + // }, + // } + // ); - assert.equal(children.mock.calls[0][0].loadedOnce, true); - }); + // expect(children.mock.calls[0][0]).toEqual({ + // currentSort: { field: 'id', order: 'ASC' }, + // loadedOnce: true, + // referenceBasePath: '', + // data: { + // 2: { + // id: 2, + // value: 'reference2', + // }, + // }, + // ids: [1, 2], + // }); + // }); it('should set the data prop to the loaded data when it has been fetched', () => { - const children = jest.fn(); - const data = { - 1: { id: 1, title: 'hello' }, - 2: { id: 2, title: 'world' }, - }; - shallow( + const children = jest.fn().mockReturnValue('child'); + renderWithRedux( {children} - + , + { + admin: { + resources: { + bar: { + data: { + 1: { id: 1, title: 'hello' }, + 2: { id: 2, title: 'world' }, + }, + }, + }, + }, + } ); - assert.equal(children.mock.calls[0][0].loadedOnce, true); - assert.deepEqual(children.mock.calls[0][0].data, data); - assert.deepEqual(children.mock.calls[0][0].ids, [1, 2]); + expect(children.mock.calls[0][0]).toEqual({ + currentSort: { field: 'id', order: 'ASC' }, + loadedOnce: true, + referenceBasePath: '', + data: { + 1: { id: 1, title: 'hello' }, + 2: { id: 2, title: 'world' }, + }, + ids: [1, 2], + }); }); it('should support record with string identifier', () => { - const children = jest.fn(); - const data = { - 'abc-1': { id: 'abc-1', title: 'hello' }, - 'abc-2': { id: 'abc-2', title: 'world' }, - }; - shallow( + const children = jest.fn().mockReturnValue('child'); + renderWithRedux( {children} - + , + { + admin: { + resources: { + bar: { + data: { + 'abc-1': { id: 'abc-1', title: 'hello' }, + 'abc-2': { id: 'abc-2', title: 'world' }, + }, + }, + }, + }, + } ); - assert.equal(children.mock.calls[0][0].loadedOnce, true); - assert.deepEqual(children.mock.calls[0][0].data, data); - assert.deepEqual(children.mock.calls[0][0].ids, ['abc-1', 'abc-2']); + expect(children.mock.calls[0][0]).toEqual({ + currentSort: { field: 'id', order: 'ASC' }, + loadedOnce: true, + referenceBasePath: '', + data: { + 'abc-1': { id: 'abc-1', title: 'hello' }, + 'abc-2': { id: 'abc-2', title: 'world' }, + }, + ids: ['abc-1', 'abc-2'], + }); }); - it('should support record with number identifier', () => { - const children = jest.fn(); - const data = { - 1: { id: 1, title: 'hello' }, - 2: { id: 2, title: 'world' }, - }; - shallow( + it('should dispatch crudGetManyAccumulate', () => { + const children = jest.fn().mockReturnValue('child'); + const { dispatch } = renderWithRedux( {children} - + , + { + admin: { + resources: { + bar: { + data: {}, + }, + }, + }, + } ); - assert.equal(children.mock.calls[0][0].loadedOnce, true); - assert.deepEqual(children.mock.calls[0][0].data, data); - assert.deepEqual(children.mock.calls[0][0].ids, [1, 2]); + expect(dispatch).toBeCalledWith(crudGetManyAccumulate('bar', [1, 2])); }); }); diff --git a/packages/ra-core/src/controller/field/ReferenceArrayFieldController.tsx b/packages/ra-core/src/controller/field/ReferenceArrayFieldController.tsx index 546c5aa9c9..8f6130b6c9 100644 --- a/packages/ra-core/src/controller/field/ReferenceArrayFieldController.tsx +++ b/packages/ra-core/src/controller/field/ReferenceArrayFieldController.tsx @@ -13,8 +13,6 @@ interface ChildrenFuncParams { interface Props { basePath: string; children: (params: ChildrenFuncParams) => ReactNode; - data?: RecordMap; - ids: Identifier[]; record?: Record; reference: string; resource: string; @@ -59,6 +57,7 @@ const ReferenceArrayFieldController: FunctionComponent = ({ basePath, record, source, + children, }) => { return children(useReferenceArray({ resource, From efe73470eae0d0e70cd350d902fb97ee2dfd8d81 Mon Sep 17 00:00:00 2001 From: thiery Date: Sun, 2 Jun 2019 20:37:57 +0200 Subject: [PATCH 5/6] use useRefeenceArray hhoks into ReferenceArrayField --- .../ReferenceArrayFieldController.spec.tsx | 140 +++++++++--------- .../ra-core/src/controller/field/index.ts | 2 + .../src/field/ReferenceArrayField.js | 15 +- 3 files changed, 78 insertions(+), 79 deletions(-) diff --git a/packages/ra-core/src/controller/field/ReferenceArrayFieldController.spec.tsx b/packages/ra-core/src/controller/field/ReferenceArrayFieldController.spec.tsx index d114d4ec80..5c11f5cffc 100644 --- a/packages/ra-core/src/controller/field/ReferenceArrayFieldController.spec.tsx +++ b/packages/ra-core/src/controller/field/ReferenceArrayFieldController.spec.tsx @@ -7,80 +7,80 @@ import { crudGetManyAccumulate } from '../../actions'; describe('', () => { afterEach(cleanup); - // it('should set the loadedOnce prop to false when related records are not yet fetched', () => { - // const children = jest.fn().mockReturnValue('child'); + it('should set the loadedOnce prop to false when related records are not yet fetched', () => { + const children = jest.fn().mockReturnValue('child'); - // renderWithRedux( - // - // {children} - // , - // { - // admin: { - // resources: { - // bar: { - // data: {}, - // }, - // }, - // }, - // } - // ); - // expect(children.mock.calls[0][0]).toEqual({ - // currentSort: { field: 'id', order: 'ASC' }, - // loadedOnce: false, - // referenceBasePath: '', - // data: null, - // ids: [1, 2], - // }); - // }); + renderWithRedux( + + {children} + , + { + admin: { + resources: { + bar: { + data: {}, + }, + }, + }, + } + ); + expect(children.mock.calls[0][0]).toEqual({ + currentSort: { field: 'id', order: 'ASC' }, + loadedOnce: false, + referenceBasePath: '', + data: null, + ids: [1, 2], + }); + }); - // it('should set the loadedOnce prop to true when at least one related record is found', () => { - // const children = jest.fn().mockReturnValue('child'); + it('should set the loadedOnce prop to true when at least one related record is found', () => { + const children = jest.fn().mockReturnValue('child'); - // renderWithRedux( - // - // {children} - // , - // { - // admin: { - // resources: { - // bar: { - // data: { - // 2: { - // id: 2, - // value: 'reference2', - // }, - // }, - // }, - // }, - // }, - // } - // ); + renderWithRedux( + + {children} + , + { + admin: { + resources: { + bar: { + data: { + 2: { + id: 2, + value: 'reference2', + }, + }, + }, + }, + }, + } + ); - // expect(children.mock.calls[0][0]).toEqual({ - // currentSort: { field: 'id', order: 'ASC' }, - // loadedOnce: true, - // referenceBasePath: '', - // data: { - // 2: { - // id: 2, - // value: 'reference2', - // }, - // }, - // ids: [1, 2], - // }); - // }); + expect(children.mock.calls[0][0]).toEqual({ + currentSort: { field: 'id', order: 'ASC' }, + loadedOnce: true, + referenceBasePath: '', + data: { + 2: { + id: 2, + value: 'reference2', + }, + }, + ids: [1, 2], + }); + }); it('should set the data prop to the loaded data when it has been fetched', () => { const children = jest.fn().mockReturnValue('child'); diff --git a/packages/ra-core/src/controller/field/index.ts b/packages/ra-core/src/controller/field/index.ts index 9022a0d35e..18f2ec85d9 100644 --- a/packages/ra-core/src/controller/field/index.ts +++ b/packages/ra-core/src/controller/field/index.ts @@ -2,8 +2,10 @@ import ReferenceArrayFieldController from './ReferenceArrayFieldController'; import ReferenceFieldController from './ReferenceFieldController'; import ReferenceManyFieldController from './ReferenceManyFieldController'; import useReference from './useReference'; +import useReferenceArray from './useReferenceArray'; export { + useReferenceArray, ReferenceArrayFieldController, ReferenceFieldController, useReference, diff --git a/packages/ra-ui-materialui/src/field/ReferenceArrayField.js b/packages/ra-ui-materialui/src/field/ReferenceArrayField.js index 24255b44d1..0fe5376803 100644 --- a/packages/ra-ui-materialui/src/field/ReferenceArrayField.js +++ b/packages/ra-ui-materialui/src/field/ReferenceArrayField.js @@ -2,7 +2,7 @@ import React, { Children } from 'react'; import PropTypes from 'prop-types'; import LinearProgress from '@material-ui/core/LinearProgress'; import { withStyles, createStyles } from '@material-ui/core/styles'; -import { ReferenceArrayFieldController } from 'ra-core'; +import { useReferenceArray } from 'ra-core'; import { fieldPropTypes } from './types'; const styles = createStyles({ @@ -85,14 +85,11 @@ export const ReferenceArrayField = ({ children, ...props }) => { } return ( - - {controllerProps => ( - - )} - + ); }; From 39ada08af080f4251c01dca886cab0c792d6b84d Mon Sep 17 00:00:00 2001 From: thiery Date: Sun, 2 Jun 2019 20:52:46 +0200 Subject: [PATCH 6/6] add jsdoc --- .../ReferenceArrayFieldController.spec.tsx | 4 +- .../field/ReferenceArrayFieldController.tsx | 27 +++++---- .../src/controller/field/useReferenceArray.ts | 57 +++++++++---------- 3 files changed, 46 insertions(+), 42 deletions(-) diff --git a/packages/ra-core/src/controller/field/ReferenceArrayFieldController.spec.tsx b/packages/ra-core/src/controller/field/ReferenceArrayFieldController.spec.tsx index 5c11f5cffc..5de569df05 100644 --- a/packages/ra-core/src/controller/field/ReferenceArrayFieldController.spec.tsx +++ b/packages/ra-core/src/controller/field/ReferenceArrayFieldController.spec.tsx @@ -59,7 +59,7 @@ describe('', () => { data: { 2: { id: 2, - value: 'reference2', + title: 'hello', }, }, }, @@ -75,7 +75,7 @@ describe('', () => { data: { 2: { id: 2, - value: 'reference2', + title: 'hello', }, }, ids: [1, 2], diff --git a/packages/ra-core/src/controller/field/ReferenceArrayFieldController.tsx b/packages/ra-core/src/controller/field/ReferenceArrayFieldController.tsx index 8f6130b6c9..14decb7a28 100644 --- a/packages/ra-core/src/controller/field/ReferenceArrayFieldController.tsx +++ b/packages/ra-core/src/controller/field/ReferenceArrayFieldController.tsx @@ -1,6 +1,7 @@ -import { FunctionComponent, ReactNode, useEffect, ReactElement } from 'react'; +import { FunctionComponent, ReactNode, ReactElement } from 'react'; -import useReferenceArray from './useReferenceArray' +import useReferenceArray from './useReferenceArray'; +import { Identifier, RecordMap, Record, Sort } from '../..'; interface ChildrenFuncParams { loadedOnce: boolean; @@ -59,13 +60,19 @@ const ReferenceArrayFieldController: FunctionComponent = ({ source, children, }) => { - return children(useReferenceArray({ - resource, - reference, - basePath, - record, - source, - })) as ReactElement; + return children({ + currentSort: { + field: 'id', + order: 'ASC', + }, + ...useReferenceArray({ + resource, + reference, + basePath, + record, + source, + }), + }) as ReactElement; }; -export default ReferenceArrayFieldController; \ No newline at end of file +export default ReferenceArrayFieldController; diff --git a/packages/ra-core/src/controller/field/useReferenceArray.ts b/packages/ra-core/src/controller/field/useReferenceArray.ts index 9f369c3f31..4065939b78 100644 --- a/packages/ra-core/src/controller/field/useReferenceArray.ts +++ b/packages/ra-core/src/controller/field/useReferenceArray.ts @@ -12,7 +12,6 @@ interface ReferenceArrayProps { ids: Identifier[]; data: RecordMap; referenceBasePath: string; - currentSort: Sort; } interface Option { @@ -24,36 +23,38 @@ interface Option { } /** - * A container component that fetches records from another resource specified + * @typedef ReferenceArrayProps + * @type {Object} + * @property {boolean} loadedOnce: boolean indicating if the reference has already beeen loaded + * @property {Array} ids: the list of ids. + * @property {Object} data: Object holding the reference data by their ids + * @property {string} referenceBasePath basePath of the reference + */ + +/** + * Hook that fetches records from another resource specified * by an array of *ids* in current record. * - * You must define the fields to be passed to the iterator component as children. + * @example * - * @example Display all the products of the current order as datagrid - * // order = { - * // id: 123, - * // product_ids: [456, 457, 458], - * // } - * - * - * - * - * - * - * - * + * const { loadedOnce, data, ids, referenceBasePath, currentSort } = useReferenceArray({ + * basePath: 'resource'; + * record: { referenceIds: ['id1', 'id2']}; + * reference: 'reference'; + * resource: 'resource'; + * source: 'referenceIds'; + * }); * - * @example Display all the categories of the current product as a list of chips - * // product = { - * // id: 456, - * // category_ids: [11, 22, 33], - * // } - * - * - * - * - * + * @param {Object} option + * @param {boolean} option.allowEmpty do we allow for no referenced record (default to false) + * @param {string} option.basePath basepath to current resource + * @param {string | false} option.linkType The type of the link toward the referenced record. edit, show of false for no link (default to edit) + * @param {Object} option.record The The current resource record + * @param {string} option.reference The linked resource name + * @param {string} option.resource The current resource name + * @param {string} option.source The key of the linked resource identifier * + * @returns {ReferenceProps} The reference props */ const useReferenceArray = ({ resource, @@ -79,10 +80,6 @@ const useReferenceArray = ({ ids, data, referenceBasePath, - currentSort: { - field: 'id', - order: 'ASC', - }, }; };