在 React JSX 中有条件地渲染组件部分
Conditionally rendering component sections in React JSX
根据 MDN "You can also do more than one single operation per case, separating them with a comma." 下面的示例有效:
var stop = false, age = 23;
age > 18 ? (
alert("1"),
alert("2")
) : (
stop = true,
alert("Sorry, you are much too young!")
);
但我似乎无法在 React 中执行相同操作,如下所示。我希望同时显示 "Yes" 和 "No" 按钮,但它只显示 "No" 按钮。
return (
<div className="topcoat-list">
<ul className="topcoat-list__container">
{
notes.map(function (note) {
var title = note.content.substring(0, note.content.indexOf("\n"));
title = title || note.content;
var toggleDeleteDialogs = this.state.isConfirming && note.id === notepad.selectedId;
var disableDelete = this.state.isConfirming && note.id !== notepad.selectedId;
return (
<li key={note.id} onClick={this.onSelectNote.bind(null, note.id)} className="topcoat-list__item">
{title}
{
toggleDeleteDialogs ?
(
<button key={note.id} onClick={this.deleteThisNote.bind(null, note.id)} className="half">Yes</button>,
<button className="half" onClick={this.onCancelDelete}>No</button>
) : (
<button key={note.id} onClick={this.deleteThisNote.bind(null, note.id)} className="full" disabled={disableDelete ? "disabled" : ""}>Delete Note</button>
)
}
</li>
);
}.bind(this))
}
</ul>
</div>
);
完整标记:https://jsfiddle.net/55fvpcLo/
我的语法有问题还是可以更优雅地完成?
fiddle 似乎不起作用,但我可以重现该行为。尽管它不会引发 Adjacent JSX elements must be wrapped in an enclosing tag
错误,但我怀疑这可能是它不起作用的原因,因为相邻元素实际上是您要尝试执行的操作。
我认为最简单的解决方案是将两个元素包裹在一个封闭的标记中而不是括号中。
您还可以 return JSX 组件数组,例如
{
toggleDeleteDialogs ?
[<Button ... />, <Button ... />] :
<Button .../>
}
@Adam Stone 是对的,问题是相邻的 JSX 元素没有包含在结束标记中。
也就是说,你问的是解决问题的最优雅的方法。
我对您的代码进行了以下更改:
使用此函数有选择地隐藏 JSX 元素:
var hideIfFalse=function(boolean){
return boolean? {} : {display : 'none'};
};
你可以这样使用:
<div style={hideIfFalse(toggleDeleteDialogs)} />
将用于呈现列表项的逻辑分离到 renderChildren
方法中:
renderChildren:function(notes,classes){
return notes.map(function (note) {
//...
制作了一个DeleteDialog
组件。它具有自己的渲染逻辑的可重用功能,将其分离可提高代码可读性:
var DeleteDialog=React.createClass({
render:function(){
var classes=this.props.classes;
return <div style={hideIfFalse(this.props.toggleDeleteDialogs)}>
<button onClick={this.props.onDelete} className="half">
Yes
</button>,
<button className="half" onClick={this.props.onCancelDelete}>
No
</button>
</div>
}
});
我没有接触 classSet
逻辑,但不明白它应该做什么。
var hideIfFalse=function(boolean){
return boolean? {} : {display : 'none'};
};
var notepad = {
notes:
[
{
id: 1,
content: "Hello, world!\nBoring.\nBoring.\nBoring."
},
{
id: 2,
content: "React is awesome.\nSeriously, it's the greatest."
},
{
id: 3,
content: "Robots are pretty cool.\nRobots are awesome, until they take over."
},
{
id: 4,
content: "Monkeys.\nWho doesn't love monkeys?"
}
],
selectedId: 1
};
var DeleteDialog=React.createClass({
render:function(){
var classes=this.props.classes;
return <div style={hideIfFalse(this.props.toggleDeleteDialogs)}>
<button onClick={this.props.onDelete} className="half">
Yes
</button>,
<button className="half" onClick={this.props.onCancelDelete}>
No
</button>
</div>
}
})
var NotesList = React.createClass({
getInitialState: function() {
return {
isConfirming: false
};
},
onSelectNote: function(id) {
notepad.selectedId = id;
},
deleteThisNote: function(noteId) {
if(this.state.isConfirming) {
// actual delete functionality should be here
this.setState({isConfirming: false});
}
else {
this.setState({isConfirming: true});
}
},
onCancelDelete: function() {
this.setState({ isConfirming: false });
},
renderChildren:function(notes,classes){
return notes.map(function (note) {
var title = note.content.substring(0, note.content.indexOf("\n"));
title = title || note.content;
var toggleDeleteDialogs = this.state.isConfirming && note.id === notepad.selectedId;
var disableDelete = this.state.isConfirming && note.id !== notepad.selectedId;
return <li key={note.id}
onClick={this.onSelectNote.bind(null, note.id)}
className="topcoat-list__item">
{title}
<button key={note.id} onClick={this.deleteThisNote.bind(null, note.id)} className="full" disabled={disableDelete ? "disabled" : ""}>Delete Note</button>
<DeleteDialog
toggleDeleteDialogs={toggleDeleteDialogs}
note={note}
onDelete={this.deleteThisNote.bind(null, note.id)}
onCancelDelete={this.onCancelDelete.bind(this)} />
</li>
}.bind(this))
},
render: function() {
var notes = notepad.notes;
var cx = React.addons.classSet;
var classes = cx({
"topcoat-button-bar__button": true,
"full": !this.state.isConfirming,
"half": this.state.isConfirming,
});
return (
<div className="topcoat-list">
<ul className="topcoat-list__container">
{this.renderChildren(notes,classes)}
</ul>
</div>
);
}
});
React.render(<NotesList />, document.getElementById('container'));
JSFiddle:http://jsfiddle.net/55fvpcLo/2/
根据 MDN "You can also do more than one single operation per case, separating them with a comma." 下面的示例有效:
var stop = false, age = 23;
age > 18 ? (
alert("1"),
alert("2")
) : (
stop = true,
alert("Sorry, you are much too young!")
);
但我似乎无法在 React 中执行相同操作,如下所示。我希望同时显示 "Yes" 和 "No" 按钮,但它只显示 "No" 按钮。
return (
<div className="topcoat-list">
<ul className="topcoat-list__container">
{
notes.map(function (note) {
var title = note.content.substring(0, note.content.indexOf("\n"));
title = title || note.content;
var toggleDeleteDialogs = this.state.isConfirming && note.id === notepad.selectedId;
var disableDelete = this.state.isConfirming && note.id !== notepad.selectedId;
return (
<li key={note.id} onClick={this.onSelectNote.bind(null, note.id)} className="topcoat-list__item">
{title}
{
toggleDeleteDialogs ?
(
<button key={note.id} onClick={this.deleteThisNote.bind(null, note.id)} className="half">Yes</button>,
<button className="half" onClick={this.onCancelDelete}>No</button>
) : (
<button key={note.id} onClick={this.deleteThisNote.bind(null, note.id)} className="full" disabled={disableDelete ? "disabled" : ""}>Delete Note</button>
)
}
</li>
);
}.bind(this))
}
</ul>
</div>
);
完整标记:https://jsfiddle.net/55fvpcLo/
我的语法有问题还是可以更优雅地完成?
fiddle 似乎不起作用,但我可以重现该行为。尽管它不会引发 Adjacent JSX elements must be wrapped in an enclosing tag
错误,但我怀疑这可能是它不起作用的原因,因为相邻元素实际上是您要尝试执行的操作。
我认为最简单的解决方案是将两个元素包裹在一个封闭的标记中而不是括号中。
您还可以 return JSX 组件数组,例如
{
toggleDeleteDialogs ?
[<Button ... />, <Button ... />] :
<Button .../>
}
@Adam Stone 是对的,问题是相邻的 JSX 元素没有包含在结束标记中。
也就是说,你问的是解决问题的最优雅的方法。
我对您的代码进行了以下更改:
使用此函数有选择地隐藏 JSX 元素:
var hideIfFalse=function(boolean){ return boolean? {} : {display : 'none'}; };
你可以这样使用:
<div style={hideIfFalse(toggleDeleteDialogs)} />
将用于呈现列表项的逻辑分离到
renderChildren
方法中:renderChildren:function(notes,classes){ return notes.map(function (note) { //...
制作了一个
DeleteDialog
组件。它具有自己的渲染逻辑的可重用功能,将其分离可提高代码可读性:var DeleteDialog=React.createClass({ render:function(){ var classes=this.props.classes; return <div style={hideIfFalse(this.props.toggleDeleteDialogs)}> <button onClick={this.props.onDelete} className="half"> Yes </button>, <button className="half" onClick={this.props.onCancelDelete}> No </button> </div> } });
我没有接触
classSet
逻辑,但不明白它应该做什么。
var hideIfFalse=function(boolean){
return boolean? {} : {display : 'none'};
};
var notepad = {
notes:
[
{
id: 1,
content: "Hello, world!\nBoring.\nBoring.\nBoring."
},
{
id: 2,
content: "React is awesome.\nSeriously, it's the greatest."
},
{
id: 3,
content: "Robots are pretty cool.\nRobots are awesome, until they take over."
},
{
id: 4,
content: "Monkeys.\nWho doesn't love monkeys?"
}
],
selectedId: 1
};
var DeleteDialog=React.createClass({
render:function(){
var classes=this.props.classes;
return <div style={hideIfFalse(this.props.toggleDeleteDialogs)}>
<button onClick={this.props.onDelete} className="half">
Yes
</button>,
<button className="half" onClick={this.props.onCancelDelete}>
No
</button>
</div>
}
})
var NotesList = React.createClass({
getInitialState: function() {
return {
isConfirming: false
};
},
onSelectNote: function(id) {
notepad.selectedId = id;
},
deleteThisNote: function(noteId) {
if(this.state.isConfirming) {
// actual delete functionality should be here
this.setState({isConfirming: false});
}
else {
this.setState({isConfirming: true});
}
},
onCancelDelete: function() {
this.setState({ isConfirming: false });
},
renderChildren:function(notes,classes){
return notes.map(function (note) {
var title = note.content.substring(0, note.content.indexOf("\n"));
title = title || note.content;
var toggleDeleteDialogs = this.state.isConfirming && note.id === notepad.selectedId;
var disableDelete = this.state.isConfirming && note.id !== notepad.selectedId;
return <li key={note.id}
onClick={this.onSelectNote.bind(null, note.id)}
className="topcoat-list__item">
{title}
<button key={note.id} onClick={this.deleteThisNote.bind(null, note.id)} className="full" disabled={disableDelete ? "disabled" : ""}>Delete Note</button>
<DeleteDialog
toggleDeleteDialogs={toggleDeleteDialogs}
note={note}
onDelete={this.deleteThisNote.bind(null, note.id)}
onCancelDelete={this.onCancelDelete.bind(this)} />
</li>
}.bind(this))
},
render: function() {
var notes = notepad.notes;
var cx = React.addons.classSet;
var classes = cx({
"topcoat-button-bar__button": true,
"full": !this.state.isConfirming,
"half": this.state.isConfirming,
});
return (
<div className="topcoat-list">
<ul className="topcoat-list__container">
{this.renderChildren(notes,classes)}
</ul>
</div>
);
}
});
React.render(<NotesList />, document.getElementById('container'));
JSFiddle:http://jsfiddle.net/55fvpcLo/2/