【问题标题】:How to pass props only to first element in array of React elements?如何仅将道具传递给 React 元素数组中的第一个元素?
【发布时间】:2019-07-22 18:26:14
【问题描述】:

如何仅将道具传递给未知元素的 React 元素数组中的第一个元素?例如,在下面的 sn-p 中,elements 是一个最多包含 3 个自定义 Bar 元素的数组,我希望只向第一个传递任意 prop。

function Foo(props) {
  const myProp = 'test';
  const elements = [
    (isP && <Bar prop1={'p'} />),
    (isQ && <Bar prop1={'q'} />),
    (isR && <Bar prop1={'r'} />),
  ].filter(x => x);
  // Now do something like 'elements[0].props.myProp = myProp'
  return elements;
}

上下文是我想在侧边栏中显示三个面板。第一个应显示其标题和正文,其他应仅显示其标题。

我考虑过但不喜欢的一些解决方案:

1) 使用 React 的 cloneElement 方法克隆第一个元素并将其传递给 prop。 1 但是克隆一个 React 元素只是为了设置一个 prop 似乎很糟糕。

2) 预处理布尔逻辑以确定哪个元素将是第一个,然后分配道具。例如,除了更聪明的逻辑之外,类似于下面的代码:

function Foo(props) {
  const myProp = 'test';
  const elements = [
    (isP && <Bar myProp={isP && myProp}/>),
    (isQ && <Bar myProp={!isP && isQ && myProp} />),
    (isR && <Bar myProp={!isP && !isQ && isR && myProp} />),
  ].filter(x => x);
  return elements;
}

理想情况下,解决方案将高效而简洁地实现目标。如果没有比 (1) 或 (2) 更好的解决方案,我也会接受它作为答案。

编辑:有些地方我忘了澄清。

  • 我正在使用 Redux,以防万一考虑到这一点。
  • &lt;Bar /&gt; 的实例实际上是多种类型(例如BarBasBaz),以防严重影响答案。 (我错误地过度简化了原始问题。)

【问题讨论】:

  • 您只想创建一个&lt;Bar/&gt;
  • 否,0-3 个条形元素,取决于 isP、isQ 和 isR 的值。
  • 在(最多)3 个中,只有第一个应该有 myProp?
  • 是的,没错。
  • isP等的值是多少?布尔值? undefined 还是真的?如果 myProp 在你不想要的时候是 falsy 可以吗?

标签: javascript reactjs


【解决方案1】:

我想我会保持简单并使用push

function Foo(props) {
  const myProp = 'test';
  const elements = [];
  if (isP) {
      elements.push(<Bar myProp={myProp}/>);
  }
  if (isQ) {
      elements.push(elements.length ? <Bar/> : <Bar myProp={myProp}/>);
  }
  if (isR) {
      elements.push(elements.length ? <Bar/> : <Bar myProp={myProp}/>);
  }
  return elements;
}

如果myProp 可以是虚假的,那么最后两个会变得更简单:

  // ...
  if (isQ) {
      elements.push(<Bar myProp={elements.length || myProp}/>);
  }
  // ...

如果 falsy 不行,但 undefined 是:

  // ...
  if (isQ) {
      elements.push(<Bar myProp={elements.length ? undefined : myProp}/>);
  }
  // ...

当然它不是特别漂亮。 :-) 或者循环:

function Foo(props) {
  const myProp = 'test';
  const elements = [];
  for (const flag of [isP, isQ, isR]) {
      if (flag) {
          elements.push(elements.length ? <Bar/> : <Bar myProp={myProp}/>);
      }
  }
  return elements;
}

(再次根据myProp 的规则,可能避免使用条件运算符。)

如果您需要prop1'p''q''r',这在问题的第一个代码块中而不是第二个代码块中,可以通过对象数组上的循环轻松完成:

function Foo(props) {
  const myProp = 'test';
  const elements = [];
  const specs = [
    [isP, 'p'],
    [isQ, 'q'],
    [isR, 'r']
  ];
  for (const [flag, prop1] of specs) {
      if (flag) {
          elements.push(elements.length ? <Bar prop1={prop1}/> : <Bar prop1={prop1} myProp={myProp}/>);
      }
  }
  return elements;
}

(再次根据myProp 的规则,可能避免使用条件运算符。)

等等

【讨论】:

    【解决方案2】:

    我不禁认为这是XY Problem。如果您向我们展示实际代码,我们可能会更有效地帮助您。

    无论如何,如果我们在黑暗中刺伤,这是另一种使用解构赋值的方法。如果isPisQisR都为false,则返回一个空数组。

    const Foo = (props) =>
    { const specs =
        [ [ isP, 'p' ]
        , [ isQ, 'q' ]
        , [ isR, 'r' ]
        ]
    
      const [ first, ...rest ] =
        specs.reduce
          ( (acc, [ flag, prop ]) =>
              flag ? [ ...acc, prop ] : acc
          , []
          )
    
      if (first === undefined)
        return []
    
      return (
        [ <Bar myProp={myProp} prop={first} />
        , ...rest.map (prop => <Bar prop={prop} />)
        ]
      )
    }
    

    您说过组件和道具可以针对每种情况而有所不同。在这种情况下,您可以修改程序以支持您的独特需求 -

    const Foo = (props) =>
    { const specs =
        [ [ isP, Bar, { prop: 'r' } ]
        , [ isQ, Qux, { prop: 'q', another: 'a' } ]
        , [ isR, Rox, { prop: 'r' } ]
        ]
    
      return specs
        .filter
          ( ([ flag, _0, _1 ]) => flag
          )
        .map
          ( ([ _, Comp, props ], i) =>
              i === 0
                ? <Comp {...props} myProp={myProp} />
                : <Comp {...props} />
          )
    }
    

    【讨论】:

    • 我的代码只是有不同的 isP、isQ、isR 值,而 的每个实例实际上是应用了许多特定道具的不同类型的组件。否则它是相同的。
    • @CeasarBautista 我更新了答案。下次用真实代码发个问题。
    • 会的。我试图让每个人都知道额外的细节,但不知道 XY 问题。感谢您提出!
    • 别担心,我很乐意为您提供帮助。很高兴您的问题最终得到了解决。
    【解决方案3】:

    正如使用redux 所指出的,理想情况下,源数据应通过调用reducer 中的函数来预先计算,以使数组具有正确的长度,并在需要时设置props。这将意味着组件内部的不必要计算必须在任何组件更新时呈现。

    myArr: [
      { id: 0, someProp: { msg: 'hello' } },
      { id: 1: someProp: {} },
      { id: 2: someProp: {} }
    ]
    

    myArr: [
      { id: 1, someProp: { msg: 'hello' } },
      { id: 2: someProp: {} }
    ]
    

    然后组件只需要做

    const Foo = (props) => {
      const { myArr } = props;
      return (
        <div>
          {
            myArr.map(item => (
              <Bar key={item.id} {...item.someProp} />
            ))
          }
        </div>
      );
    };
    

    编辑:如果需要不同的类型,可以在一个通用的 lib 文件中使用以下内容。

    export const COMPONENT_TYPE = {
      BAR: 'BAR',
      BAS: 'BAS',
      BAZ: 'BAZ'
    };
    

    现在

    import { COMPONENT_TYPE: { BAR, BAS, BAZ } } from '../common';
    
    myArr: [
      { id: 0, someProp: { msg: 'hello' }, componentType: BAR  },
      { id: 1: someProp: {}, componentType: BAS },
      { id: 2: someProp: {}, componentType: BAZ }
    ]
    

    现在在我们的组件中

    import { COMPONENT_TYPE: { BAR, BAS, BAZ } } from '../common';
    
    const Foo = (props) => {
      const { myArr } = props;
      return (
        <div>
          {
            myArr.map(item => (
              <div key={item.id}>
                { 
                  item.componentType === BAR &&
                  <Bar {...item.someProp} />
                }
                { 
                  item.componentType === BAS &&
                  <Bas {...item.someProp} />
                }
                { 
                  item.componentType === BAZ &&
                  <Baz {...item.someProp} />
                }
              </div>
            ))
          }
        </div>
      );
    };
    

    这 3 个单独的条件句的执行几乎与 if/else if/else 完全相同,而且更加清晰。如果您的代码将有BAR/BAS/BAZ 如此相似,您可以这样做

    const Component = item.componentType;
    <Component {...item.someProp} />
    

    map里面只要把枚举从大写改成实际的组件名

    【讨论】:

    • 诀窍是你事先不知道你将包括三个中的哪一个,所以你不能依赖i === 0
    • 理想情况下,这种条件逻辑不应该存在于组件中。它应该在state 或任何正在使用的商店中,并在那里订购,并在那里添加任何属性。组件应该只需要渲染它
    • 大概它状态,只是为了问题的目的而简化了。
    • 我的意思是说在redux的上下文中,这个逻辑应该只在reducer中确定,以便组件Foo只接收数据并且只需要应用简单的布尔值,而不是执行for循环和其他可能复杂的逻辑
    • @tic 您能否在回答中解释一下您将如何重组代码以将条件逻辑移出?听起来这可能会更好,但我想看看它的比较。
    猜你喜欢
    • 1970-01-01
    • 2021-12-21
    • 2020-11-24
    • 2018-06-20
    • 1970-01-01
    • 1970-01-01
    • 2016-04-29
    • 2019-05-02
    • 1970-01-01
    相关资源
    最近更新 更多