I feel I just reinvented the wheel and it might not be round, because I'm new to the React ecosystem.
I have written these components :
const ForwardPropsWrapper = ({ children, ...rest }) => {
return React.Children.map(children, child => React.cloneElement(child, rest))
}
Note that it just forwards its props to its children.
const ConditionalWrapper = ({ condition, children, ...rest }) => {
return (!condition || condition(rest)) ?
React.Children.map(children, child => React.cloneElement(child, rest))
: null;
}
condition
is a function and it's passed the wrapper's props
I use it with react-admin
's to replace a ReferenceField
inside a Datagrid
with two fields combined :
<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>
Why ?
ForwardPropsWrapper
because react-admin
's Datagrid
expects one child per column and passes props to it (the record). A React.Fragment
is not good enough because it will swallow up the props.
ConditionalWrapper
is explicit, I need to show either one of the two components depending on the props that Datagrid
passes them. The condition needs to be evaluated with the props that are passed to the wrapped component by Datagrid
so I can't use a simple ternary condition in the template.
So... I can't believe this is the way to go.
Is there a way to achieve this without writing custom components ?
What problems may I run into with the components above ?
Criticism is expected please !
Well I was right saying this was all useless. Just needed to create a custom Field component.
<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 }
</>
)
}