如何将道具仅传递给React元素数组中的第一个元素?

时间:2019-02-28 18:56:41

标签: javascript reactjs

如何将prop只传递给未知元素的React元素数组中的第一个元素?例如,在以下代码段中,elements是最多3个自定义的Bar元素的数组,我只希望第一个传递给任意道具。

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元素只是为了设置一个道具似乎很糟糕。

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以防万一。
  • <Bar />的实例实际上是多种类型(例如BarBasBaz),以免对答案产生重大影响。 (我错误地简化了原始问题。)

3 个答案:

答案 0 :(得分:2)

我想我会保持简单并使用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}/>);
  }
  // ...

如果不正确,但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'的{​​{1}},而不是第二个代码块,则只需使用遍历对象数组:

'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; } 的规则避免使用条件运算符。)

等等。

答案 1 :(得分:1)

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

无论如何,如果我们在黑暗中刺伤,这是使用销毁分配的另一种方法。如果isPisQisR均为假,将返回一个空数组。

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

答案 2 :(得分:0)

如正在使用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