我试图从帮助器方法返回多个React元素。我可以通过移动一些代码来解决它,但我想知道是否有更清晰的方法来解决它。我有一个方法返回render
方法的一部分,并且该函数需要返回一个React元素和一些文本。通过一个例子,它更清楚:
class Foo extends React.Component {
_renderAuthor() {
if (!this.props.author) {
return null;
}
return [
' by ',
<a href={getAuthorUrl(this.props.author)}>{this.props.author}</a>,
]; // Triggers warning: Each child in an array or iterator should have a unique "key" prop.
render() {
return (
<div>
{this.props.title}
{this._renderAuthor()}
</div>
);
}
}
我知道render
方法必须返回1个React元素。使用这样的辅助方法会触发警告,修复警告(通过添加键)会使代码过于复杂。是否有一种干净的方法可以在不触发警告的情况下执行此操作?
修改
另一个用例:
render() {
return (
<div>
{user
? <h2>{user.name}</h2>
<p>{user.info}</p>
: <p>User not found</p>}
</div>
);
}
编辑2:
事实证明这还不可能,我在这里写了大约2个解决方法:https://www.wptutor.io/web/js/react-multiple-elements-without-wrapper
答案 0 :(得分:9)
错误消息告诉您如何解决此问题:
数组或迭代器中的每个子项都应该有一个唯一的&#34;键&#34;丙
而不是:
flags
这样做:
return [
' by ',
<a href={getAuthorUrl(this.props.author)}>{this.props.author}</a>,
];
是的,您需要在一个范围中包装文本节点(&#34; by&#34;)以便为其提供密钥。这就是休息时间。正如您所看到的,我只是给每个元素一个静态return [
<span key="by"> by </span>,
<a key="author" href={getAuthorUrl(this.props.author)}>{this.props.author}</a>,
];
,因为它们没有任何动态。如果您愿意,也可以使用key
和key="1"
。
或者,你可以这样做:
key="2"
...不需要return <span> by <a href={getAuthorUrl(this.props.author)}>{this.props.author}</a></span>;
s。
以下是工作片段中的前一个解决方案:
key
&#13;
const getAuthorUrl = author => `/${author.toLowerCase()}`;
class Foo extends React.Component {
_renderAuthor() {
if (!this.props.author) {
return null;
}
return [
<span key="by"> by </span>,
<a key="author" href={getAuthorUrl(this.props.author)}>{this.props.author}</a>,
];
}
render() {
return (
<div>
{this.props.datePosted}
{this._renderAuthor()}
</div>
);
}
}
ReactDOM.render(<Foo datePosted="Today" author="Me"/>, document.getElementById('container'));
&#13;
答案 1 :(得分:6)
使用Fragment组件添加了支持。这是一流的组件。
所以你现在可以使用:
render() {
return (
<React.Fragment>
<ChildA />
<ChildB />
<ChildC />
</React.Fragment>
);
}
有关详细信息,请访问:https://reactjs.org/blog/2017/11/28/react-v16.2.0-fragment-support.html
答案 2 :(得分:5)
如果没有某种解决方法,例如将所有内容包装在另一个组件中,目前无法做到这一点,因为它最终会导致底层的React代码尝试返回多个对象。
请参阅this active Github issue,其中对未来版本的支持正在考虑中。
编辑:您可以现在使用React 16中的Fragments执行此操作,请参阅: https://reactjs.org/blog/2017/11/28/react-v16.2.0-fragment-support.html
答案 3 :(得分:1)
还有另一种解决方法。我建议你创建另一个组件Author.js:
function Author(props) {
return (<span>
<span> by </span>
<a href={props.getAuthorUrl(props.author)}>{props.author}</a>
</span>)
}
class Foo extends React.Component {
render() {
return (
<div>
{this.props.title}
{this.props.author && <Author author={this.props.author} getAuthorUrl={this.getAuthorUrl} />}
</div>
);
}
}
我没有测试过这段代码。但我认为它看起来会更清洁。希望它有所帮助。
答案 4 :(得分:0)
我喜欢为这些东西设置一个If组件,并且我已将所有东西都包裹在一个范围内,因为它并没有真正破坏任何东西并且需要密钥消失......
const getAuthorUrl = author => `/${author.toLowerCase()}`;
function If({condition,children}) {
return condition ? React.Children.only(children) : null;
}
class Foo extends React.Component {
render() {
return (
<div>
{this.props.datePosted}
<If condition={this.props.author}>
<span> by
<a key="author" href={getAuthorUrl(this.props.author)}>
{this.props.author}
</a>
</span>
</If>
</div>
);
}
}
ReactDOM.render(<Foo datePosted="Today" author="Me"/>, document.getElementById('container'));
&#13;
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/15.1.0/react.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/15.1.0/react-dom.min.js"></script>
<div id="container"></div>
&#13;
...完全跳过阵列的事情?
答案 5 :(得分:0)
这有点hacky但是没有你想要的不必要的jsx。
var author = 'Daniel';
var title = 'Hello';
var Hello = React.createClass({
_renderAutho0r: function() {
if (!author) {
return null;
}
return <a href="#">{author}</a>
},
render: function() {
var by = author ? ' by ' : null;
return (
<div>
{title}
{by}
{this._renderAutho0r()}
</div>
);
}
});
React.render(<Hello name="World" />, document.body);
我的JSFiddle
答案 6 :(得分:0)
您可以从子渲染函数返回片段,但不能从主渲染函数返回,至少在React 16之前。为此,返回一个组件数组。您不需要手动设置密钥,除非您的片段子项将更改(默认情况下数组使用索引键入)。
要创建片段,您还可以使用createFragment
。
对于内联使用,您可以使用数组或利用立即调用的箭头函数。 请参阅以下示例:
const getAuthorUrl = author => `/${author.toLowerCase()}`;
class Foo extends React.Component {
constructor() {
super();
this._renderAuthor = this._renderAuthor.bind(this);
this._renderUser = this._renderUser.bind(this);
}
_renderAuthor() {
if (!this.props.author) {
return null;
}
return [
' by ',
<a href={getAuthorUrl(this.props.author)}>{this.props.author}</a>,
];
}
_renderUser() {
return [
<h2>{this.props.user.name}</h2>,
<p>{this.props.user.info}</p>
]
}
render() {
return (
<div>
{this.props.datePosted}
{this._renderAuthor()}
<div>
{this.props.user
? this._renderUser()
: <p>User not found</p>}
</div>
<div>
{this.props.user
? [
<h2>{this.props.user.name}</h2>,
<p>{this.props.user.info}</p>
]
: <p>User not found</p>}
</div>
<div>
{this.props.user
? (() => [
<h2>{this.props.user.name}</h2>,
<p>{this.props.user.info}</p>
])()
: <p>User not found</p>}
</div>
</div>
);
}
}
ReactDOM.render(<Foo datePosted="Today" author="Me" user={{name: 'test', info: 'info'}} />, document.getElementById('container'));
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/15.1.0/react.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/15.1.0/react-dom.min.js"></script>
<div id="container"></div>
为了不发出警告,必须为每个孩子分配一个钥匙。为了做到这一点,请使用帮助函数fragment(...children)
自动分配基于索引的键,而不是返回数组。请注意,必须将字符串转换为可以使用密钥分配的跨度或其他节点:
const fragment = (...children) =>
children.map((child, index) =>
React.cloneElement(
typeof child === 'string'
? <span>{child}</span>
: child
, { key: index }
)
)
const getAuthorUrl = author => `/${author.toLowerCase()}`;
const fragment = (...children) =>
children.map((child, index) =>
React.cloneElement(
typeof child === 'string'
? <span>{child}</span>
: child
, { key: index }
)
)
class Foo extends React.Component {
constructor() {
super();
this._renderAuthor = this._renderAuthor.bind(this);
this._renderUser = this._renderUser.bind(this);
}
_renderAuthor() {
if (!this.props.author) {
return null;
}
return fragment(
' by ',
<a href={getAuthorUrl(this.props.author)}>{this.props.author}</a>
);
}
_renderUser() {
return fragment(
<h2>{this.props.user.name}</h2>,
<p>{this.props.user.info}</p>
)
}
render() {
return (
<div>
{this.props.datePosted}
{this._renderAuthor()}
<div>
{this.props.user
? this._renderUser()
: <p>User not found</p>}
</div>
<div>
{this.props.user
? fragment(
<h2>{this.props.user.name}</h2>,
<p>{this.props.user.info}</p>
)
: <p>User not found</p>}
</div>
<div>
{this.props.user
? (() => fragment(
<h2>{this.props.user.name}</h2>,
<p>{this.props.user.info}</p>
))()
: <p>User not found</p>}
</div>
</div>
);
}
}
ReactDOM.render(<Foo datePosted="Today" author="Me" user={{name: 'test', info: 'info'}} />, document.getElementById('container'));
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/15.1.0/react.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/15.1.0/react-dom.min.js"></script>
<div id="container"></div>
答案 7 :(得分:0)
试试这个:
class Foo extends React.Component {
_renderAuthor() {
return <a href={getAuthorUrl(this.props.author)}>{this.props.author}</a>
}
render() {
return (
<div>
{this.props.title}
{this.props.author && " by "}
{this.props.author && this._renderAuthor()}
</div>
);
}
}
答案 8 :(得分:0)
或许更简单的方法是重新思考如何构建应用程序。但是,以一种更简单的方式。
你正在触发警告,因为你试图从数组渲染而不是反应元素而是直接使用html。为了解决这个问题,你必须这样做
{this._renderAuthor().map(
(k,i) => (React.addons.createFragment({k}))
) }
React addons
createFragment
函数基本上就是这样,它会将你的html元素减少为你可以呈现的反应片段。
React createFragment documentation
或者,在更好的方法中,您可以像这样创建一个AuthorLink stateless
组件。
function AuthorLink(props) {
return (
<div className="author-link">
<span> by </span>
<a href={props.authorUrl}> {props.author} </a>
</div>
});
}
并在主要组件的渲染中使用它
render() {
const { author } = this.props;
return (
<div>
{this.props.datePosted}
<AuthorLink url={getAuthorUrl(author)} author={author} />
</div>
);
}
答案 9 :(得分:-4)
在你的阵列上尝试这种方法:
return [
<span key={'prefix-'+random_string_generator()}>' by '</span>,
<a key={'prefix-'+random_string_generator()} href={getAuthorUrl(this.props.author)}>{this.props.author}</a>,
];