React为已渲染的组件指定键

时间:2017-09-14 13:26:02

标签: reactjs key render

有可能吗?

我有一个组件,其中子项由props的任意映射函数呈现。一个简化的例子:

class SomeComponent extends Component {
  render() {
    const { renderChild, businessObjects } = this.props
    return <div>
       {businessObjects.map(renderChild)}
    </div>
  }
}

我明显收到一条警告,说明孩子的呈现时没有key属性。

我尝试在渲染vdom元素后分配键:

...
{
  businessObjects.map(e => {
    const vdom = renderChild(e)
    vdom.key = e.id
    return vdom
  })
}
...

但是从JSX转换返回的对象被冻结了,所以我无法做到这一点。此外,没有API可以临时解冻然后重新冻结js中的对象。出于性能原因克隆是不可能的(数千个组件都是这样渲染的)

我该怎么办?

同样,出于性能原因,我无法将渲染的子项包装到另一个组件中,因此这样的解决方案无法工作:

const Child = ({renderChild, bo}) => (<div>{renderChild(bo)}</div>)

// in SomeComponent
...
{
  businessObjects.map(e => (<Child 
    key={e.id}
    bo={e} 
    renderChild={renderChild} 
  />)
  )
}
...

更新 这种结构的原因是SomeComponent是一个哑组件,并且无法访问应用程序状态(redux)。但渲染的孩子确实需要访问dispatch(我是以连接动作创建者的形式进行访问)。

所以你可以想象这样的事情:

const createChildRenderer = ({actionFoo, actionBar}) => (obj) => {
  switch(obj.type) {
    case FOO:
      return <div onClick={() => actionFoo()}>{obj.text}</div>
    case BAR:
      return <div onClick={() => actionBar()}>{obj.text}</div>
    default:
      return null
  }
}

在连接组件中

@connect(
  ({ businessObjects }) => { businessObjects },
  { actionFoo, actionBar}
)
class SmartComponent extends Component {
  render() {
    const renderChild = createChildRenderer({
      actionFoo: this.props.actionFoo, // action creators
      actionBar: this.props.actionBar
    })
    return (<SomeComponent 
       renderChild={renderChild} 
       businessObjects={this.props.businessObjects}>
  }
}

2 个答案:

答案 0 :(得分:0)

您可以对从renderChild收到的孩子使用cloneElement

React.cloneElement(
  child,
  {...child.props, key: yourKeyValue}
)

答案 1 :(得分:0)

我通过将实际的反应组件作为参数来解决这个问题的方式:

因此,在之前采用渲染器功能的哑组件中,现在我采用了一个组件:

class SomeComponent extends Component {
  render() {
    const { ChildComponent, businessObjects } = this.props
    return <div>
       {businessObjects.map((o) => (<ChildComponent 
           businessObject={o}
           key={o.id}
       />)}
    </div>
  }
}

我之前创建了渲染器函数,现在我创建了组件:

const createChildComponent = ({actionFoo, actionBar}) => 
  ({ businessObject: obj }) => { // this is now a component created dynamically
    switch(obj.type) {
      case FOO:
        return <div onClick={() => actionFoo()}>{obj.text}</div>
      case BAR:
        return <div onClick={() => actionBar()}>{obj.text}</div>
      default:
        return null
    }
}

在连接组件中:

@connect(
  ({ businessObjects }) => { businessObjects },
  { actionFoo, actionBar}
)
class SmartComponent extends Component {
  render() {
    const ChildComponent = createChildComponent({
      actionFoo: this.props.actionFoo, // action creators
      actionBar: this.props.actionBar
    })
    return (<SomeComponent 
       ChildComponent={ChildComponent} 
       businessObjects={this.props.businessObjects}>
  }
}