代码之家  ›  专栏  ›  技术社区  ›  Armel Larcier

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

  •  1
  • Armel Larcier  · 技术社区  · 7 年前

    我觉得我只是重新发明了轮子,它可能不是圆的,因为我对这个生态系统还不熟悉。

    我编写了以下组件:

    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 反应管理员 的 期望 一 每列的子项,并将道具传递给它(记录)。A. React.Fragment 还不够好,因为它会吞掉道具。

    ConditionalWrapper 如果是明确的,我需要显示两个组件中的任何一个,这取决于所使用的道具 通过他们。需要使用传递给包装组件的道具对条件进行评估 所以我不能在模板中使用简单的三元条件。

    所以我不敢相信这是一条路。

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

    以上组件可能会出现什么问题?

    欢迎批评!

    2 回复  |  直到 7 年前
        1
  •  2
  •   Estus Flask    7 年前

    考虑到 <ForwardPropsWrapper label="User / Role">

    const commonProps = { label: 'User / Role' };
    
    ...
    
    <Foo {...commonProps} foo={'bar'} />
    <Foo {...commonProps} foo={'baz'} />
    

    或作为临时:

    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

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

    这里的实际问题是 Datagrid

    render prop ,或者更具体地说,它的特例, 童心 数据网格 可提供以代替原始组件使用:

    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>
    
        2
  •  2
  •   Armel Larcier    7 年前

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

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