我需要这些手工制作的组件吗(React.Fragment将通过道具和条件包装)



我觉得我只是重新发明了轮子,它可能不是圆形的,因为我是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是一个函数,它传递了包装器的props

我将它与react-admin的一起使用,将Datagrid中的ReferenceField替换为两个组合字段:

<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-adminDatagrid期望每列有一个子项,并将props传递给它(记录)。React.Fragment不够好,因为它会吞噬道具。

ConditionalWrapper是显式的,我需要根据Datagrid传递给它们的道具来显示这两个组件中的任何一个。该条件需要使用Datagrid传递给包装组件的props进行评估,因此我不能在模板中使用简单的三元条件。

所以。。。我简直不敢相信这是要走的路。

有没有一种方法可以在不编写自定义组件的情况下实现这一点?

上面的组件可能会遇到什么问题?

请批评!

我说得对,这些都没用。只需要创建一个自定义字段组件。

<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 }
</>
)
}

考虑到<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用于向儿童提供数据的配方是不灵活的,遍历儿童并为他们提供额外的道具。这使得在需要处理道具的情况下需要繁琐的包装。

实现相同目标的一种更灵活、更常用的模式是渲染道具,或者更具体地说,它的特殊情况,作为子项的功能。可以提供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>

最新更新