一、JSX 中的 If-Else
没法在JSX
中使用 if-else
语句,因为 JSX
只是函数调用和对象创建的语法糖。
可以采用三元操作表达式:
React.render(<div id={condition ? 'msg' : ''}>Hello World!</div>, mountNode);
当三元操作表达式不够健壮,你也可以使用 if 语句来决定应该渲染那个组件。
var loginButton;
if (loggedIn) {
loginButton = <LogoutButton />;
} else {
loginButton = <LoginButton />;
}
return (
<nav>
<Home />
{loginButton}
</nav>
)
二、自闭合标签
在 JSX
中, <MyComponent />
是合法的,而 <MyComponent>
就不合法。 所有的标签都必须闭合,可以是自闭和的形式,也可以是常规的闭合。
注意:
所有React component
都可以采用自闭和的形式:<div />. <div></div>
它们是等价的。
三、JSX 根节点的最大数量
目前, 一个 component
的 render
,只能返回一个节点。如果你需要返回一堆 div
, 那你必须将你的组件用 一个div
或 span
或任何其他的组件包裹。
切记,JSX
会被编译成常规的 JS
; 因此返回两个函数也就没什么意义了,同样地,千万不要在三元操作符中放入超过一个子节点。
四、Controlled Input 值为 null 的情况
为每个controlled component
指定 value
属性,来防止用户修改输入除非你希望如此。
也许会遇到这种问题:虽然已经指定了 value
,但是 input
依然可以未经允许就改变。这种情况,可能是因为一不小心将 value
设置成了 undefined
或 null
。
下面这条代码片段展示了这个现象,一秒钟之后,文本变得可编辑了。
React.render(<input value="hi" />, mountNode);
setTimeout(function() {
React.render(<input value={null} />, mountNode);
}, 1000);
五、Mounting 后 componentWillReceiveProps 未被触发
当节点初次被放入的时候 componentWillReceiveProps
并不会被触发。这是故意这么设计的。
原因是因为 componentWillReceiveProps
经常会处理一些和 old props
比较的逻辑,而且会在变化之前执行;不在组件即将渲染的时候触发,这也是这个方法设计的初衷。
六、getInitialState 里的 Props 是一个反模式
使用 props
, 自父级向下级传递,在使用 getInitialState
生成 state
的时候,经常会导致重复的”来源信任”,i.e. 如果有可能,请尽量在使用的时候计算值,以此来确保不会出现同步延迟的问题和状态保持的问题。
糟糕的例子
var MessageBox = React.createClass({
getInitialState: function() {
return {nameWithQualifier: 'Mr. ' + this.props.name};
},
render: function() {
return <div>{this.state.nameWithQualifier}</div>;
}
});
React.render(<MessageBox name="Rogers"/>, mountNode);
更好的写法:
var MessageBox = React.createClass({
render: function() {
return <div>{'Mr. ' + this.props.name}</div>;
}
});
React.render(<MessageBox name="Rogers"/>, mountNode);
对于更复杂的逻辑,最好通过方法将数据处理分离开来,然而,如果你理清了这些,那么它也就 不是 反模式了。两者兼得不是我们的目标:
var Counter = React.createClass({
getInitialState: function() {
// naming it initialX clearly indicates that the only purpose
// of the passed down prop is to initialize something internally
return {count: this.props.initialCount};
},
handleClick: function() {
this.setState({count: this.state.count + 1});
},
render: function() {
return <div onClick={this.handleClick}>{this.state.count}</div>;
}
});
React.render(<Counter initialCount={7}/>, mountNode);
六、组件的 DOM 事件监听
注意:
下面讲的是如何给DOM
元素绑定React
未提供的事件。 当你想和其他类库比如jQuery
一起使用的时候,需要知道这些。
//Try to resize the window:
var Box = React.createClass({
getInitialState: function() {
return {windowWidth: window.innerWidth};
},
handleResize: function(e) {
this.setState({windowWidth: window.innerWidth});
},
componentDidMount: function() {
window.addEventListener('resize', this.handleResize);
},
componentWillUnmount: function() {
window.removeEventListener('resize', this.handleResize);
},
render: function() {
return <div>Current window width: {this.state.windowWidth}</div>;
}
});
React.render(<Box />, mountNode);
componentDidMount
会在 component
渲染完成且已经有了 DOM
结构的时候被调用。通常情况下,你可以在这绑定普通的 DOM
事件。
注意,事件的回调被绑定在了 react 组件上,而不是原始的元素上。React 通过一个 autobinding 过程自动将方法绑定到当前的组件实例上。
七、JSX 的 false 处理
下面展示了在不同上下文中 false
的渲染:
a、被渲染成 *id=”false”:*
React.render(<div id={false} />, mountNode);
b、String “false
” as input value:
input value 的值将会是 “false
” 字符串
React.render(<input value={false} />, mountNode);
c、没有子节点
React.render(<div>{false}</div>, mountNode);
上面这个没有被渲染成 false
字符串是因为要考虑到这种常见的情况:<div>{x > 1 && 'You have more than one item'}</div>
.
//Demo
var App = React.createClass({
componentDidMount: function() {
// This doesn't refer to the `span`s! It refers to the children between
// last line's `<App></App>`, which are undefined.
console.log(this.props.children);
},
render: function() {
var x = 2;
return <div id={false}><span>{false}</span> <span>{x>1 && 'You have more than one item'}</span> <input type="text" value={false} /></div>;
}
});
//生成标签代码
<div id="demo5">
<div id="false" data-reactid=".3">
<span data-reactid=".3.0"></span>
<span data-reactid=".3.1"> </span>
<span data-reactid=".3.2">You have more than one item</span>
<span data-reactid=".3.3"> </span>
<input type="text" value="false" data-reactid=".3.4">
</div>
</div>
八、组件间的通信
对于 父-子 通信,直接 pass props.
对于 子-父 通信: 例如: GroceryList
组件有一些通过数组生成的子节点。当这些节点被点击的时候,你想要展示这个节点的名字:
var GroceryList = React.createClass({
handleClick: function(i) {
console.log('You clicked: ' + this.props.items[i]);
},
render: function() {
return (
<div>
{this.props.items.map(function(item, i) {
return (
<div onClick={this.handleClick.bind(this, i)} key={i}>{item}</div>
);
}, this)}
</div>
);
}
});
React.render(
<GroceryList items={['Apple', 'Banana', 'Cranberry']} />, mountNode
);
注意
bind(this, arg1, arg2, ...)
的使用: 我们通过它向handleClick
传递参数。 这不是React
的新概念,而是JavaScript
的。
对于没有 父-子 关系的组件间的通信,你可以设置你自己的全局事件系统。 在 componentDidMount()
里订阅事件,在 componentWillUnmount()
里退订,然后在事件回调里调用 setState()
。
九、组件的引用
如果你正在一个大型的非 React
应用里使用 React
组件,或者准备将你的代码转换成 React
,你可能需要保持组件的引用。 React.render
会返回一个渲染后的组件的引用:
var myComponent = React.render(<MyComponent />, myContainer);
记住,JSX
并不会返回组件的引用! 它只是一个 ReactElement
: 一个用来告知 React
渲染后的组件应该长什么样子的轻便的标识符。
var myComponentElement = <MyComponent />; // 只是 ReactElement.
// Some code here...
var myComponentInstance = React.render(myComponentElement, myContainer);
注意:
这中引用只能在最顶层级使用。 在组件内部,让props
和state
来处理组件间的通信,而且只能通过refs
. 来引用。
十、this.props.children undefined
你没办法通过 this.props.children
取得当前组件的子元素。 因为this.props.children
返回的是组件拥有者传递给你的 passed onto you
子节点。
var App = React.createClass({
componentDidMount: function() {
// This doesn't refer to the `span`s! It refers to the children between
// last line's `<App></App>`, which are undefined.
console.log(this.props.children);
},
render: function() {
return <div><span/><span/></div>;
}
});
React.render(<App></App>, mountNode);
如果想看更多地例子, 可以参考在 front page 里最后一个例子。
十一、与其他类库并行使用React
你不用非得全部采用 React
。组件的 生命周期事件,特别是componentDidMount
和 componentDidUpdate
,非常适合放置其他类库的逻辑代码。
var App = React.createClass({
getInitialState: function() {
return {myModel: new myBackboneModel({items: [1, 2, 3]})};
},
componentDidMount: function() {
$(this.refs.placeholder.getDOMNode()).append($('<span />'));
},
componentWillUnmount: function() {
// Clean up work here.
},
shouldComponentUpdate: function() {
// Let's just never update this component again.
return false;
},
render: function() {
return <div ref="placeholder"/>;
}
});
React.render(<App />, mountNode);
你还可以通过这种方式来绑定你自己的 事件监听(event listeners) 甚至是 事件流(event streams)。
十二、Dangerously Set innerHTML
var FormClass5 = React.createClass({
render: function () {
var style = {marginTop: 10};
function createMarkup(){
return {__html: "First & middot; Second"}
}
return (
<div>
<div dangerouslySetInnerHTML={createMarkup()}></div>
<select multiple={true} value={['B','C']} style={style}>
<option value="A">Apple</option>
<option value="B">Banana</option>
<option value="C">Cranberry</option>
</select>
</div>
);
}
});
React.render(
<FormClass5 />,
document.getElementById('demo5')
);
生成html:
<div id="demo5">
<div data-reactid=".4">
<div data-reactid=".4.0">First & middot; Second</div>
<select multiple="" style="margin-top:10px;" data-reactid=".4.1">
<option value="A" data-reactid=".4.1.0">Apple</option>
<option value="B" data-reactid=".4.1.1">Banana</option>
<option value="C" data-reactid=".4.1.2">Cranberry</option>
</select>
</div>
</div>
function createMarkup() { return {__html: 'First · Second'}; };
<div dangerouslySetInnerHTML={createMarkup()} />
这么做的意义在于,当你不是有意地使用 <div dangerouslySetInnerHTML={getUsername()} />
时候,它并不会被渲染,因为 getUsername() 返回的格式是 字符串 而不是一个 {__html: ”} 对象。{__html:…} 背后的目的是表明它会被当成 “type/taint” 类型处理。 这种包裹对象,可以通过方法调用返回净化后的数据,随后这种标记过的数据可以被传递给 dangerouslySetInnerHTML
。 基于这种原因,我们不推荐写这种形式的代码:<div dangerouslySetInnerHTML={{__html: getMarkup()}} />
.
这个功能主要被用来与 DOM
字符串操作类库一起使用,所以提供的 HTML
必须要格式清晰(例如:传递 XML
校验 )