将键分配给已渲染的组件
React assign key to already rendered component
可能吗?
我有一个组件,其中 children 由作为 props
传入的任意映射函数呈现。一个简化的例子:
class SomeComponent extends Component {
render() {
const { renderChild, businessObjects } = this.props
return <div>
{businessObjects.map(renderChild)}
</div>
}
}
我显然收到一条警告,说 children 在没有 key
属性的情况下呈现。
我尝试在呈现 vdom 元素后分配密钥:
...
{
businessObjects.map(e => {
const vdom = renderChild(e)
vdom.key = e.id
return vdom
})
}
...
但是从 JSX 转换返回的 object 被冻结了,所以我不能这样做。也没有API临时解冻然后re-freezeobjects在js中。出于性能原因,克隆是不可能的(成千上万的组件是这样呈现的)
我能做什么?
同样,出于性能原因,我不能将渲染的 children 包装到另一个组件中,因此这样的解决方案行不通:
const Child = ({renderChild, bo}) => (<div>{renderChild(bo)}</div>)
// in SomeComponent
...
{
businessObjects.map(e => (<Child
key={e.id}
bo={e}
renderChild={renderChild}
/>)
)
}
...
更新
这种结构的原因是 SomeComponent
是一个哑组件,无法访问应用程序状态 (redux)。但是呈现的 children 确实需要访问 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}>
}
}
您可以在从 renderChild
收到的 child 上使用 cloneElement:
React.cloneElement(
child,
{...child.props, key: yourKeyValue}
)
我最终通过将实际的反应组件作为参数来解决这个问题的方式:
所以在以前带渲染器功能的哑组件中,现在我带一个组件:
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}>
}
}
可能吗?
我有一个组件,其中 children 由作为 props
传入的任意映射函数呈现。一个简化的例子:
class SomeComponent extends Component {
render() {
const { renderChild, businessObjects } = this.props
return <div>
{businessObjects.map(renderChild)}
</div>
}
}
我显然收到一条警告,说 children 在没有 key
属性的情况下呈现。
我尝试在呈现 vdom 元素后分配密钥:
...
{
businessObjects.map(e => {
const vdom = renderChild(e)
vdom.key = e.id
return vdom
})
}
...
但是从 JSX 转换返回的 object 被冻结了,所以我不能这样做。也没有API临时解冻然后re-freezeobjects在js中。出于性能原因,克隆是不可能的(成千上万的组件是这样呈现的)
我能做什么?
同样,出于性能原因,我不能将渲染的 children 包装到另一个组件中,因此这样的解决方案行不通:
const Child = ({renderChild, bo}) => (<div>{renderChild(bo)}</div>)
// in SomeComponent
...
{
businessObjects.map(e => (<Child
key={e.id}
bo={e}
renderChild={renderChild}
/>)
)
}
...
更新
这种结构的原因是 SomeComponent
是一个哑组件,无法访问应用程序状态 (redux)。但是呈现的 children 确实需要访问 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}>
}
}
您可以在从 renderChild
收到的 child 上使用 cloneElement:
React.cloneElement(
child,
{...child.props, key: yourKeyValue}
)
我最终通过将实际的反应组件作为参数来解决这个问题的方式:
所以在以前带渲染器功能的哑组件中,现在我带一个组件:
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}>
}
}