一、在getInitialState裏邊使用 props 是一種反面模式
提示:這不是React明確規定的,就像經常發生在其它代碼中的那樣;在這種情況下,React能使這種模式更清晰。
在getInitialState
中使用props傳遞的父組件的數據來綁定state,經常會導致所謂的真理的重複來源(就是說可能分不清數據來源了)。只要有可能動態推斷值,以確定不會發生不同步以及維護困難的情況。(小僧翻譯出來也快吐了,原文如果不引入俚語還好,一旦用了就暈乎了,不過在最下方小僧會大致總結下,客官勿急)。
反面示例:
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);
個人總結:getInitialState中初始化的數據,原則是保持在該組件及其子組件使用,不要將父組件的數據(props傳遞過來的數據)使用在這裏。如果你想在該組件內使用的數據需要根據props傳遞過來的數據來初始化,那就是你的數據原型有問題了,爲什麼不在其它方法中直接使用父組件的數據。與父組件通信,最好是通過回調函數,該回調函數是從父組件傳遞過來,並且裏邊封裝了state綁定的數據(即更改的數據)。
二、在組件中使用DOM事件
注意:這裏說的事件是非React提供的事件。React有關於DOM的操作可以很容易和其它類庫合作,如jQuery。
試着改變窗口大小:
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
會在組件被渲染之後被調用。在這個方法中通常被用來給DOM添加事件,因爲只有DOM(渲染成功)存在,才能給其添加事件。
注意這裏的事件是綁定到了React中的組件,而不是最原始的DOM。React會通過一個叫 autobinding 的過程將這個事件方法綁定到組件中。
三、用ajax加載初始數據
在componentDidMount
中獲取數據:當響應到達時,會將數據存儲的state中,然後觸發更新UI。
當處理異步請求的響應時,要確定當前組件是否仍然在更新中,通過 this.isMounted() 來判斷。
下邊的例子是獲取所需的Github用戶最新動態:
var UserGist = React.createClass({
getInitialState: function() {
return {
username: '',
lastGistUrl: ''
};
},
componentDidMount: function() {
$.get(this.props.source, function(result) {
var lastGist = result[0];
if (this.isMounted()) {
this.setState({
username: lastGist.owner.login,
lastGistUrl: lastGist.html_url
});
}
}.bind(this));
},<pre name="code" class="javascript">React.render(<div id={false} />, mountNode);
render: function() { return ( <div> {this.state.username}'s last gist is <a href={this.state.lastGistUrl}>here</a>. </div> ); }});React.render( <UserGist source="https://api.github.com/users/octocat/gists" />, mountNode); 注意:例子中的bind(this),只能用在函數
四、在JSX中使用false
下邊是不同情況系 false 渲染的過程:
1. 渲染成id=“false”:
React.render(<div id={false} />, mountNode);
2. 渲染成輸入框中的字符串值React.render(<input value={false} />, mountNode);
3. 沒有子元素的情況React.render(<div>{false}</div>, mountNode);
在這種情況中,false被渲染成<div></div>之間的文本,而是作爲一個div的子元素,是爲了可以有更多靈活的用法:如<div>{x > 1 && 'You have more than one item'}</div>
五、組件間的通信
在父子組件之間的通信,可以簡單的通過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就有的。
在兩個同級子組件之間的通信,你可以設置一個全局的事件componentWillUnmount
。在 componentDidMount 中註冊監聽,在
componentWillUnmount
卸載監聽,當監聽到一個事件時,調用setState()更新視圖。
六、暴露組件的功能
組件之間的通信也有一個不常見的方法:可以簡單的在子組件中調用父組件的回調函數,此處的子組件拆分更加瑣碎。
下邊的例子中,每點擊一個條目然後自身被刪除,當僅剩一個條目時,就爲其加上動畫效果。
var Todo = React.createClass({
render: function() {
return <div onClick={this.props.onClick}>{this.props.title}</div>;
},
//this component will be accessed by the parent through the `ref` attribute
animate: function() {
console.log('Pretend %s is animating', this.props.title);
}
});
var Todos = React.createClass({
getInitialState: function() {
return {items: ['Apple', 'Banana', 'Cranberry']};
},
handleClick: function(index) {
var items = this.state.items.filter(function(item, i) {
return index !== i;
});
this.setState({items: items}, function() {
if (items.length === 1) {
this.refs.item0.animate();
}
}.bind(this));
},
render: function() {
return (
<div>
{this.state.items.map(function(item, i) {
var boundClick = this.handleClick.bind(this, i);
return (
<Todo onClick={boundClick} key={i} title={item} ref={'item' + i} />
);
}, this)}
</div>
);
}
});
React.render(<Todos />, mountNode);
或者,你也可以通過在子組件 todo 中定義一個 isLastUnfinishedItem 屬性,然後在
componentDidUpdate
進行判斷,是否添加動畫效果。然而,當有多個屬性來控制動畫時就顯得混亂了。
七、組件引用
當你在一個大的非React應用中想要使用React的組件或者將你的代碼過渡到React,你需要想辦法引用組件。React.render
方法返回的就是一個完整組件的引用。
var myComponent = React.render(<MyComponent />, myContainer);
要記住,JSX語法並不會返回一個組件實例。它只是一個React元素:一個組件的輕量級表現方式。
var myComponentElement = <MyComponent />; // This is just a ReactElement.
// Some code here...
var myComponentInstance = React.render(myComponentElement, myContainer);
注意:這裏只能在頂級作用域(組件外部)中使用。在組件內部應該使用 props 和 state 來保持組件間的通信。
八、this.props.children 未定義
你不能使用this.props.children來訪問組件的子節點。this.props.children被設計用來表示自身的節點。
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);
想要訪問子節點,就要使用在span中ref