import { Component, cloneElement, Children } from 'react'; import get from 'lodash/get'; import pure from 'recompose/pure'; import { Identifier } from 'ra-core'; import { FieldProps, InjectedFieldProps, fieldPropTypes } from './types'; interface State { data: object; ids: Identifier[]; } const initialState = { data: {}, ids: [], }; /** * Display a collection * * Ideal for embedded arrays of objects, e.g. * { * id: 123 * tags: [ * { name: 'foo' }, * { name: 'bar' } * ] * } * * The child must be an iterator component * (like or ). * * @example Display all the backlinks of the current post as a * // post = { * // id: 123 * // backlinks: [ * // { * // date: '2012-08-10T00:00:00.000Z', * // url: 'http://example.com/foo/bar.html', * // }, * // { * // date: '2012-08-14T00:00:00.000Z', * // url: 'https://blog.johndoe.com/2012/08/12/foobar.html', * // } * // ] * // } * * * * * * * * @example Display all the tags of the current post as components * // post = { * // id: 123 * // tags: [ * // { name: 'foo' }, * // { name: 'bar' } * // ] * // } * * * * * * * If you need to render a collection in a custom way, it's often simpler * to write your own component: * * @example * const TagsField = ({ record }) => ( *
    * {record.tags.map(item => ( *
  • {item.name}
  • * ))} *
* ) * TagsField.defaultProps = { addLabel: true }; */ export class ArrayField extends Component< FieldProps & InjectedFieldProps, State > { constructor(props: FieldProps & InjectedFieldProps) { super(props); this.state = props.record ? this.getDataAndIds(props.record, props.source) : initialState; } componentWillReceiveProps( nextProps: FieldProps & InjectedFieldProps, prevProps: FieldProps & InjectedFieldProps ) { if (nextProps.record !== prevProps.record) { this.setState( this.getDataAndIds(nextProps.record, nextProps.source) ); } } getDataAndIds(record: object, source: string) { const list = get(record, source); return list ? { data: list.reduce((prev, item) => { prev[JSON.stringify(item)] = item; return prev; }, {}), ids: list.map(JSON.stringify), } : initialState; } render() { const { addLabel, basePath, children, record, sortable, source, ...rest } = this.props; const { ids, data } = this.state; return cloneElement(Children.only(children), { ids, data, isLoading: false, basePath, currentSort: {}, ...rest, }); } } const EnhancedArrayField = pure(ArrayField); EnhancedArrayField.defaultProps = { addLabel: true, }; EnhancedArrayField.propTypes = fieldPropTypes; EnhancedArrayField.displayName = 'EnhancedArrayField'; export default EnhancedArrayField;