我需要这些手工制作的组件吗(React.Fragment 将通过 props 和条件包装器)
Do I need these handmade components (React.Fragment that will passthrough props and conditionnal wrapper)
我觉得我只是重新发明了轮子,它可能不是圆的,因为我是 React 生态系统的新手。
我写了这些组件:
const ForwardPropsWrapper = ({ children, ...rest }) => {
return React.Children.map(children, child => React.cloneElement(child, rest))
}
请注意,它只是将其 props 转发给它的 children。
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-admin
的 Datagrid
期望每列 一个 child 并将道具传递给它(记录). A React.Fragment
不够好,因为它会吞掉道具。
ConditionalWrapper
是明确的,我需要根据 Datagrid
传递给它们的道具来显示两个组件之一。条件需要使用 Datagrid
传递给包装组件的道具进行评估,所以我不能在模板中使用简单的三元条件。
所以...我不敢相信这是要走的路。
有没有不用编写自定义组件就可以实现的方法?
我 运行 使用上述组件可能会遇到什么问题?
还望大家批评指正!
考虑到 <ForwardPropsWrapper label="User / Role">
旨在生成 DRYer 代码,它的问题是它只影响它自己的 children。如果有嵌套元素或其他元素没有共同的直接 parent,它们将保持 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'} />
或者只是保持湿润,因为常见的 label="User / Role"
属性不会使代码比其他解决方案更难阅读或更笨重。
至于ConditionalWrapper
,已经有三元和short-circuit求值了,尽量使用:
{ condition && <Foo /> }
{ condition ? (
<Bar />
) : (
<Baz />
)}
这里的实际问题是Datagrid
用来向children提供数据的配方是不灵活的,遍历children并为他们提供额外的道具。这使得在需要处理 props 时需要繁琐的包装器。
实现相同目标的更灵活和常用的模式是 render prop,或者更具体地说,它的特殊情况,函数作为 child.可以提供 Datagrid
的包装器以代替原始组件使用:
const DatagridWrapper = (children, ...props) => {
const render = React.Children.only(children);
const Body = props => render(props);
return <Datagrid {...props}><Body/></Datagrid>;
};
所有网格数据都在可以任何方式处理的回调中接收,并呈现 return 个元素。
<DatagridWrapper rowClick="edit">
{gridData => <>
{gridData.record.RoleId && (
<ReferenceField label="User / Role" source="RoleId" reference="role">
<TextField source="name" />
</ReferenceField>
...
</>}
</DatagridWrapper>
好吧,我说这些都没用是对的。只需要创建一个自定义 Field 组件。
<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 }
</>
)
}
我觉得我只是重新发明了轮子,它可能不是圆的,因为我是 React 生态系统的新手。
我写了这些组件:
const ForwardPropsWrapper = ({ children, ...rest }) => {
return React.Children.map(children, child => React.cloneElement(child, rest))
}
请注意,它只是将其 props 转发给它的 children。
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-admin
的 Datagrid
期望每列 一个 child 并将道具传递给它(记录). A React.Fragment
不够好,因为它会吞掉道具。
ConditionalWrapper
是明确的,我需要根据 Datagrid
传递给它们的道具来显示两个组件之一。条件需要使用 Datagrid
传递给包装组件的道具进行评估,所以我不能在模板中使用简单的三元条件。
所以...我不敢相信这是要走的路。
有没有不用编写自定义组件就可以实现的方法?
我 运行 使用上述组件可能会遇到什么问题?
还望大家批评指正!
考虑到 <ForwardPropsWrapper label="User / Role">
旨在生成 DRYer 代码,它的问题是它只影响它自己的 children。如果有嵌套元素或其他元素没有共同的直接 parent,它们将保持 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'} />
或者只是保持湿润,因为常见的 label="User / Role"
属性不会使代码比其他解决方案更难阅读或更笨重。
至于ConditionalWrapper
,已经有三元和short-circuit求值了,尽量使用:
{ condition && <Foo /> }
{ condition ? (
<Bar />
) : (
<Baz />
)}
这里的实际问题是Datagrid
用来向children提供数据的配方是不灵活的,遍历children并为他们提供额外的道具。这使得在需要处理 props 时需要繁琐的包装器。
实现相同目标的更灵活和常用的模式是 render prop,或者更具体地说,它的特殊情况,函数作为 child.可以提供 Datagrid
的包装器以代替原始组件使用:
const DatagridWrapper = (children, ...props) => {
const render = React.Children.only(children);
const Body = props => render(props);
return <Datagrid {...props}><Body/></Datagrid>;
};
所有网格数据都在可以任何方式处理的回调中接收,并呈现 return 个元素。
<DatagridWrapper rowClick="edit">
{gridData => <>
{gridData.record.RoleId && (
<ReferenceField label="User / Role" source="RoleId" reference="role">
<TextField source="name" />
</ReferenceField>
...
</>}
</DatagridWrapper>
好吧,我说这些都没用是对的。只需要创建一个自定义 Field 组件。
<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 }
</>
)
}