Нужны ли мне эти компоненты ручной работы (React.Fragment, который будет проходить через реквизит и условную обертку) - PullRequest
0 голосов
/ 26 января 2019

Мне кажется, я просто заново изобрел колесо, и оно может быть не круглым, потому что я новичок в экосистеме React.

Я написал эти компоненты:

const ForwardPropsWrapper = ({ children, ...rest }) => {
    return React.Children.map(children, child => React.cloneElement(child, rest))
}

Обратите внимание, что он просто передает свои реквизиты своим детям.

const ConditionalWrapper = ({ condition, children, ...rest }) => {
    return (!condition || condition(rest)) ?
    React.Children.map(children, child => React.cloneElement(child, rest))
    : null;
}

condition является функцией и передается реквизиту обертки

Я использую его с react-admin для замены ReferenceField внутри Datagrid на два поля вместе:

<Datagrid rowClick="edit">
    {/*
    `Datagrid` will pass props to `ForwardPropsWrapper`
    */}
    <ForwardPropsWrapper label="User / Role">
        {/*
        Both `ConditionalWrapper`s will receive props passed by `Datagrid`
        through `ForwardPropsWrapper` and call the `condition` function
        with these props as argument
        */}
        <ConditionalWrapper condition={props=>props.record.RoleId}>
            <ReferenceField source="RoleId" reference="role">
                <TextField source="name" />
            </ReferenceField>
        </ConditionalWrapper>
        <ConditionalWrapper condition={props=>props.record.UserId}>
            <ReferenceField source="UserId" reference="user">
                <TextField source="email" />
            </ReferenceField>
        </ConditionalWrapper>
    </ForwardPropsWrapper>
</Datagrid>

Почему?

ForwardPropsWrapper, поскольку react-admin Datagrid ожидает один дочерний элемент на столбец и передает ему реквизиты (запись). A React.Fragment недостаточно хорош, потому что он поглотит подпорки.

ConditionalWrapper явно, мне нужно показать любой из двух компонентов в зависимости от того, какие реквизиты Datagrid пропускает. Условие должно оцениваться с помощью реквизита, который передается обернутому компоненту с помощью Datagrid, поэтому я не могу использовать простое троичное условие в шаблоне.

Итак ... я не могу поверить, что это путь.

Есть ли способ достичь этого без написания пользовательских компонентов?

С какими проблемами я могу столкнуться с вышеуказанными компонентами?

Ожидается критика, пожалуйста!

Ответы [ 2 ]

0 голосов
/ 28 января 2019

Ну, я был прав, говоря, что все это бесполезно. Просто нужно создать пользовательский компонент поля.

<Datagrid rowClick="edit">
    <TextField source="id" />
    <DualReferenceField label="User / Role" />
    <TextField source="action" />
    <TextField source="subject" />
    <TextField source="criteria" />
    <TextField source="name" />
</Datagrid>
const DualReferenceField = props => {
    const hasRole = props.record.RoleId;
    const hasUser = props.record.UserId;
    return (
        <>
        { hasRole ? 
        <ReferenceField source="RoleId" reference="role" {...props}>
            <FunctionField render={(record) => {
                return (
                    <span>{record.name} <small>(role)</small></span>
                )
            }} />
        </ReferenceField>
        : null }
        { hasUser ?
        <ReferenceField source="UserId" reference="user" {...props}>
            <TextField source="email" />
        </ReferenceField>
        : null }
        </>
    )
}
0 голосов
/ 26 января 2019

Учитывая, что <ForwardPropsWrapper label="User / Role"> предназначен для создания кода DRYer, проблема в том, что он затрагивает только своих собственных потомков.Если есть вложенные или другие элементы, которые не имеют общего прямого родителя, они останутся WET.

В зависимости от случая можно записать как:

const commonProps = { label: 'User / Role' };

...

<Foo {...commonProps} foo={'bar'} />
<Foo {...commonProps} foo={'baz'} />

или какHOC:

const withLabel = (Comp, label) => props => <Comp {...props} label={label}/>

const FooWithUserRoleLabel = withLabel(Foo, 'User / Role');

...

<FooWithUserRoleLabel foo={'bar'} />
<FooWithUserRoleLabel foo={'baz'} />

Или просто оставьте WET, потому что общий атрибут label="User / Role" не делает код труднее для чтения или более громоздким, чем другие решения.

Что касается ConditionalWrapper,уже существует троичная оценка и оценка короткого замыкания, используйте их, где это возможно:

{ condition && <Foo /> }

{ condition ? (
  <Bar />
) : (
  <Baz />
)}

Фактическая проблема здесь заключается в том, что рецепт, который Datagrid использует для предоставления данных детям, является негибким, пересекающим детей и предоставляющимих с дополнительными опорами.Это делает громоздкие обертки необходимыми в случае обработки реквизита.

Более гибкий и обычно используемый шаблон для достижения той же цели - render prop , или, более конкретно, его особый случай, , выполняющий функцию потомка.Можно использовать оболочку для Datagrid, чтобы использовать вместо исходного компонента:

const DatagridWrapper = (children, ...props) => {
  const render = React.Children.only(children);
  const Body = props => render(props);

  return <Datagrid {...props}><Body/></Datagrid>;
};

Все данные сетки принимаются в обратном вызове, который может обрабатываться любым способом и возвращать элементы для визуализации.

<DatagridWrapper rowClick="edit">
  {gridData => <>
    {gridData.record.RoleId && (
      <ReferenceField label="User / Role" source="RoleId" reference="role">
        <TextField source="name" />
      </ReferenceField>
      ...
  </>}
</DatagridWrapper>
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...