參照React官網(wǎng)瘩缆,基本上就是自己直譯過來的循帐,因為看著英文很別扭
React官網(wǎng)
Component
組件分離你的UI界面為獨立的筝家,可重復(fù)利用的模塊旋讹。各模塊相互獨立的同時能夠相互利用。在某種程度上冒滩,組件有點類似JS中的函數(shù),他們接受任意的輸入(props)并且返回描述其特性的React元素浪谴。
定義一個組件最簡單的方式就是寫一個js函數(shù):
function Welcome(props) {
return <h1>Hello, {props.name}</h1>;
}你也可以使用ES6的
class
去定義一個組件
class Welcome extends React.Component {
render() {
return <h1>Hello, {this.props.name}</h1>;
}
}
元素可以代表DOM
節(jié)點开睡,也可以表示用戶已定義的組件。
const element = <div />
class Welcome extends React.Component {
render() {
return <h1>hello {this.props.name} </h1>
}
}
const element = <Welcome name='mianmian'/>
ReactDOM.render(
element,
document.getElementById('example')
)
警告:
組件的名字首字母要大寫
-
組件可以在輸出時調(diào)用其他的組件苟耻,通過這樣的方式篇恒,可以使一些抽象的組件變得具體。
class Welcome extends React.Component { render() { return <h1>hello {this.props.name} </h1> } } class App extends React.Component { render() { return ( <div> <Welcome name='zhuang' /> <Welcome name='weimian' /> </div> ) } } const element = <App /> ReactDOM.render( <App />, document.getElementById('example') )
提取組件
提取組件出來使得組件可重復(fù)利用凶杖,而且讓代碼閱讀起來比較簡潔胁艰,不會那么冗余。
class Welcome extends React.Component {
render() {
return <h1>hello {this.props.name} </h1>
}
}
class Old extends React.Component {
render() {
return <h1> {this.props.old} </h1>
}
}
class App extends React.Component {
render() {
return (
<div>
<Welcome name={this.props.name} />
<Old old={this.props.old} />
</div>
)
}
}
const element = <App />
ReactDOM.render(
<App name='zhuang' old='15'/>,
document.getElementById('example')
)
所有的React組件必須像純函數(shù)一樣不改變Props的值
State和Lifecycle
state
的特性跟props
有點類似智蝠,但是它是private和受組件控制的腾么,state
只適用于組件通過class
方式定義時的情況,所以有時要將函數(shù)式組件轉(zhuǎn)換為class式組件
添加本地的state到一個class中
在
render()
方法中用this.state.date
代替this.props.date
添加一個
class constructor
來初始化this.state
-
去掉
ReactDOM
中的props
屬性class Operation extends React.Component { constructor(props) { super(props); this.state = {date: new Date()}; } render() { return ( <div> <h1>hello world</h1> <h2>It is {this.state.date.toLocaleTimeString()}</h2> </div> ); } } const element = <Operation /> ReactDOM.render( element, document.getElementById('example') )
生命周期
在一個組件的應(yīng)用中杈湾,應(yīng)釋放組件里的資源
componentDidMount () {
}
componentWillUnmount () {
}
這些方法稱為生命周期的鉤子解虱, componentDidMount()
鉤子在組件輸出在DOM上后執(zhí)行,比如可以在 componentDidMount()
函數(shù)中加入一個計時器漆撞,在componentWillUnmount()
中銷毀殴泰。
componentDidMount() {
this.timerID = setInterval(() => this.tick(),1000);
}
componentWillUnmount() {
clearInterval(this.timerID);
}
最后,我們將實現(xiàn)tick()
方法每秒浮驳,用this.setState()
來更新state
的值
class Operation extends React.Component {
constructor(props) {
super(props);
this.state = {date: new Date()};
}
componentDidMount() {
this.timeID = setInterval( () => this.tick(),1000);
}
componentWillMount() {
clearInterval(this.timeID);
}
tick() {
this.setState({date: new Date()})
}
render() {
return (
<div>
<h1>hello world</h1>
<h2>It is {this.state.date.toLocaleTimeString()}</h2>
</div>
);
}
}
const element = <Operation />
ReactDOM.render(
element,
document.getElementById('example')
)
正確使用state
不要直接改變state
的值悍汛,要使用setState()
.
// Wrong
this.state.comment = 'Hello';
// Correct
this.setState({comment: 'Hello'});
constructor
是唯一可以分配this.state
的地方
state的更新可以是異步的
因為this.props
和this.state
更新可以是異步的,所以我們不能通過他們的值來計算下個state
至会。
// Wrong
this.setState({
counter: this.state.counter + this.props.increment,
});
如果一定要計算該怎么辦呢离咐?
可以用setState()
來接收一個函數(shù)而不是一個對象。這個函數(shù)接受先前的state
作為其第一個參數(shù)奋献,當(dāng)前的props
作為第二個參數(shù)
class Operation extends React.Component {
constructor(props) {
super(props);
var date = 0;
this.state = {date: parseInt(date) + 1};
}
componentDidMount() {
this.timeID = setInterval( () => this.tick(),1000);
}
componentWillUnmount() {
clearInterval(this.timeID);
}
tick() {
this.setState((prevState, props) => ({
date:parseInt(prevState.date) + parseInt(props.increment)
}))
}
render() {
return (
<div>
<h1>hello world</h1>
<h2>It is {this.state.date}</h2>
</div>
);
}
}
const element = <Operation increment='12'/>
ReactDOM.render(
element,
document.getElementById('example')
)
問題:
為什么
// Wrong
this.setState({
counter: this.state.counter + this.props.increment,
});
這種方式得到是一樣的結(jié)果
State Update are Merged
當(dāng)你調(diào)用setState()
時健霹,React
會合并你當(dāng)前所提供的state
對象
你可以在各自調(diào)用setState()
來更新他們各自的屬性旺上。
數(shù)據(jù)流
不論是父組件還是子組件都不知道某一個組件的狀態(tài),他們也沒有關(guān)心組件定義的方式糖埋。因此state經(jīng)常被稱為是本地的或者是封閉的宣吱。
一個組件可以選擇傳遞其state特性像props給其子組件:
<h2>It is {this.state.date.toLocaleTimeString()}.</h2>
這同樣可以工作在已定義的組件中:
<FormattedDate date={this.state.date} />
該組件將會接受一個date
在它的props
中即使不知道date
來自哪里
function FormattedDate(props) {
return <h2>It is {props.date.toLocaleTimeString()}.</h2>;
}
這樣的方式通常稱之為"由上到下"或者單向數(shù)據(jù)流。任何state
總是被一些特定的組件包含著瞳别,任何來源于state
的數(shù)據(jù)或UI只能影響在他們所在的組件內(nèi)部征候。
如果你把組件樹當(dāng)作是props
的瀑布,每個組件的state
就像多出來的水源祟敛,來源于任意的地方但以同樣的方式流動疤坝。
為了展示所有的組件都是獨立的,我們可以寫一個App
組件馆铁,來渲染三個<clock>
function App() {
return (
<div>
<Clock />
<Clock />
<Clock />
</div>
);
}
ReactDOM.render(
<App />,
document.getElementById('root')
);
每個clock
開始各自的計時器跑揉,各自更新。
在React app
中埠巨,你可以將一個無狀態(tài)的組件包裹在一個有狀態(tài)的組件里历谍,反之亦然。
處理事件
用React
元素處理事件跟在DOM
上處理事件很相似辣垒,不過有一些語法的區(qū)別
React
事件命名運用的是駝峰法望侈,而不是小寫-
運用
Jsx
你通過一個函數(shù)作為事件處理器,而不是一個字符串<button onclick="activateLasers()"> Activate Lasers </button> // slightly different in React: <button onClick={activateLasers}> Activate Lasers </button>
當(dāng)你使用React
時你一般不需要調(diào)用addEventListener
添加對DOM
元素的監(jiān)聽勋桶,而是在最初渲染元素的時候添加一個監(jiān)聽器脱衙。
條件渲染
`React`中條件渲染跟`javascript`工作的情況一樣,
class Operation extends React.Component {
constructor(props) {
super(props);
}
render() {
const isOk = this.props.isOk;
if(isOk) {
return <Add />;
}
return <FalseReturn />;
}
}
class Add extends Component {
render() {
return (<h2>ok</h2>)
}
}
class FalseReturn extends Component {
render() {
return (<h1>no</h1>)
}
}
const element = <Operation isOk='true'/>
ReactDOM.render(
element,
document.getElementById('example')
)
表格
受控組件
在html中
例驹,表格元素例如<input>
,<textarea>
,<select>
根據(jù)輸入的值維持自身的state
值捐韩,在React
中,state
保持原有組件的state
屬性眠饮,只通過setState()
來更新奥帘。
我們通過使React state
變成單一數(shù)據(jù)源的形式結(jié)合上述兩種方式。接著React
組件會在渲染表格的同時控制表格中的用戶輸入仪召。這樣寨蹋,表格元素的值受React
控制,因此就叫受控組件
例如扔茅,如果我們想要在提交的時候記錄名字已旧,我們就可以將一個表格作為一個受控組件:
class NameForm extends React.Component {
constructor(props) {
super(props);
this.state = {value: ''};
this.handleChange = this.handleChange.bind(this);
this.handleSubmit = this.handleSubmit.bind(this);
}
handleChange(event) {
this.setState({value: event.target.value});
}
handleSubmit(event) {
alert('A name was submitted: ' + this.state.value);
event.preventDefault();
}
render() {
return (
<form onSubmit={this.handleSubmit}>
<label>
Name:
<input type="text" value={this.state.value} onChange={this.handleChange} />
</label>
<input type="submit" value="Submit" />
</form>
);
}
}
由于value
的屬性被安置在我們的表格元素中,展示的value
就永遠都是this.state.value
召娜,這就構(gòu)成了React
的數(shù)據(jù)源运褪。由于handleChange
在每次點擊事件更新state
時工作,展示的value
也會隨用戶的輸入更新。
有了受控組件秸讹,每個state
變化將會與一個處理器函數(shù)聯(lián)系起來檀咙。這樣會使得修改數(shù)據(jù)和是數(shù)據(jù)生效更加直接。比如璃诀,
handleChange(event) {
this.setState({value: event.target.value.toUpperCase()});
}
The textarea Tag
在html
中弧可,一個<textarea>
元素定義其文本是通過其孩子的形式:
<textarea>
Hello there, this is some text in a text area
</textarea>
在React
中,一個<textarea>
使用的是value
屬性劣欢。通過這樣的方式棕诵,一個表格使用<textarea>
跟使用單行輸入相似。
class EssayForm extends React.Component {
constructor(props) {
super(props);
this.state = {
value: 'Please write an essay about your favorite DOM element.'
};
this.handleChange = this.handleChange.bind(this);
this.handleSubmit = this.handleSubmit.bind(this);
}
handleChange(event) {
this.setState({value: event.target.value});
}
handleSubmit(event) {
alert('An essay was submitted: ' + this.state.value);
event.preventDefault();
}
render() {
return (
<form onSubmit={this.handleSubmit}>
<label>
Name:
<textarea value={this.state.value} onChange={this.handleChange} />
</label>
<input type="submit" value="Submit" />
</form>
);
}
}
The select Tag
在html
中凿将,<select>
創(chuàng)造了一個下拉框校套,如:
<select>
<option value="grapefruit">Grapefruit</option>
<option value="lime">Lime</option>
<option selected value="coconut">Coconut</option>
<option value="mango">Mango</option>
</select>
在React中,是在根select
標簽中使用 value
牧抵,這種方式在受控組件中更方便,因為你只需要在一個地方更新笛匙,比如:
class FlavorForm extends React.Component {
constructor(props) {
super(props);
this.state = {value: 'coconut'};
this.handleChange = this.handleChange.bind(this);
this.handleSubmit = this.handleSubmit.bind(this);
}
handleChange(event) {
this.setState({value: event.target.value});
}
handleSubmit(event) {
alert('Your favorite flavor is: ' + this.state.value);
event.preventDefault();
}
render() {
return (
<form onSubmit={this.handleSubmit}>
<label>
Pick your favorite La Croix flavor:
<select value={this.state.value} onChange={this.handleChange}>
<option value="grapefruit">Grapefruit</option>
<option value="lime">Lime</option>
<option value="coconut">Coconut</option>
<option value="mango">Mango</option>
</select>
</label>
<input type="submit" value="Submit" />
</form>
);
}
}
總的來說,<input type="text">
,<textarea>
,<select>
工作的原理很相似灭忠,都是接受一個value屬性去實現(xiàn)一個受控組件膳算。
處理多輸入
當(dāng)你需要處理多輸入受控input
元素時,你可以添加一個name
屬性給每一個元素弛作,讓處理器函數(shù)決定根據(jù)event.target.name
的值來操作。
class Reservation extends React.Component {
constructor(props) {
super(props);
this.state = {
isGoing: true,
numberOfGuests: 2
};
this.handleInputChange = this.handleInputChange.bind(this);
}
handleInputChange(event) {
const target = event.target;
const value = target.type === 'checkbox' ? target.checked :target.value;
const name = target.name;
this.setState({
[name]: value
});
}
render() {
return (
<form>
<label>
Is going:
<input
name="isGoing"
type="checkbox"
checked={this.state.isGoing}
onChange={this.handleInputChange} />
</label>
<br />
<label>
Number of guests:
<input
name="numberOfGuests"
type="number"
value={this.state.numberOfGuests}
onChange={this.handleInputChange} />
</label>
</form>
);
}
}
Refs和DOM
在一般的數(shù)據(jù)流中华匾,props
是父組件與子組件交流的唯一方式映琳。為了修改子組件,你要用新的props
重新渲染一次蜘拉,盡管如此萨西,這里還有一些其他的情況你需要在數(shù)據(jù)流之外以命令式的方式改變子組件。這個被修改的孩子可以是React
組件中的一個例子旭旭,又或者是一個DOM
元素谎脯,對于這兩種情況,React
提供了一種方式去獲取持寄。
什么情況使用Refs
- 聚焦源梭,文本框選擇,媒體重播
- 觸發(fā)命令
- 與第三方DOM庫交流
避免將refs用于任何可以聲明的事件稍味,例如废麻,不要暴露
open()
和close()
方法在一個Dialog
組件中,而是傳遞一個isOpen
屬性值給它模庐。
添加一個Ref給一個DOM元素
React
支持一個你可以接觸到任何組件的特殊屬性烛愧。ref
屬性攜帶一個返回函數(shù).
當(dāng)ref
屬性運用在HTML
元素是,ref
回調(diào)函數(shù)接受一個潛在的DOM
元素作為其參數(shù)。通過ref
回調(diào)函數(shù)來設(shè)置一個class
性質(zhì)對于DOM元素是一種通用的方式怜姿。還有一種更好的方式是在ref
的回調(diào)函數(shù)設(shè)置屬性慎冤。
ref = {input => this.textInput = input}
ref和函數(shù)式組件
在沒有實例的情況下,你可能會使用ref
屬性在一個函數(shù)式組件中
function MyFunctionalComponent() {
return <input />;
}
class Parent extends React.Component {
render() {
// This will *not* work!
return (
<MyFunctionalComponent
ref={(input) => { this.textInput = input; }} />
);
}
}
如果你需要一個ref
你應(yīng)該轉(zhuǎn)化組件為class
形式沧卢。就跟當(dāng)你需要使用生命周期或者state
的時候轉(zhuǎn)化組件為class
形式一樣蚁堤。
盡管如此,你可以在一個函數(shù)式組件中使用ref
屬性只要你參考DOM元素或者class組件
function CustomTextInput(props) {
// textInput must be declared here so the ref callback can refer to it
let textInput = null;
function handleClick() {
textInput.focus();
}
return (
<div>
<input
type="text"
ref={(input) => { textInput = input; }} />
<input
type="button"
value="Focus the text input"
onClick={handleClick}
/>
</div>
);
}
不要過度使用refs
你會傾向于在應(yīng)用里使用refs搏恤。如果是這種情況违寿,花些時間想一想在組件的層級里state應(yīng)該在哪里。通常熟空,擁有state合適的地方應(yīng)該在層級的高處
警告
如果ref
回調(diào)函數(shù)被定義為一個內(nèi)部函數(shù)藤巢,在一次更新中他會被調(diào)用兩次,第一次是null
息罗,再一次是DOM
元素掂咒。你可以通過定義ref
回調(diào)函數(shù)作為一個class
的綁定方法,但是大部分情況下無關(guān)緊要
非受控組件
在大多數(shù)情況下迈喉,我們推薦使用受控組件來實現(xiàn)表格绍刮。在受控組件中,表格數(shù)據(jù)受React
組件處理挨摸。另外一種選擇是非受控組件孩革,表格數(shù)據(jù)受DOM
自身處理
在寫一個非受控組件時,你不用寫一個事件處理器為state
的更新得运,你可以使用ref
從DOM
中獲取表格數(shù)據(jù)
例子:
class NameForm extends React.Component {
constructor(props) {
super(props);
this.handleSubmit = this.handleSubmit.bind(this);
}
handleSubmit(event) {
alert('A name was submitted: ' + this.input.value);
event.preventDefault();
}
render() {
return (
<form onSubmit={this.handleSubmit}>
<label>
Name:
<input type="text" ref={(input) => this.input = input} />
</label>
<input type="submit" value="Submit" />
</form>
);
}
自從非受控組件保持了DOM
的數(shù)據(jù)源后膝蜈,會使得受控組件與非受控組件的交流變得容易。這也會使代碼變得簡潔熔掺。除此之外饱搏,你應(yīng)該常常使用受控組件
Default Values
在React
渲染的生命周期中,form
元素中的value
屬性將會被覆蓋在DOM
里置逻。有了非受控組件推沸,你可以使用React
去初始化一個值,但是讓隨后的更新不受控制券坞,為了處理這種情況鬓催,你可以特征化一個defaultValue
屬性而不是value
屬性。
render() {
return (
<form onSubmit={this.handleSubmit}>
<label>
Name:
<input
defaultValue="Bob"
type="text"
ref={(input) => this.input = input} />
</label>
<input type="submit" value="Submit" />
</form>
);
}
像這樣报慕,<input type='checkbox'>
和<input type='radio'>
支持defaultChecked
,<select>
支持defaultValue
容器
一些組件不能提前知道他們的孩子深浮,我們推薦使用一些特殊的children屬性直接輸出子元素
function FancyBorder(props) {
return (
<div className={'FancyBorder FancyBorder-' + props.color}>
{props.children}
</div>
);
}
這樣讓其他的組件通過包裹在jsx
傳遞任意的子元素給他們
function WelcomeDialog() {
return (
<FancyBorder color="blue">
<h1 className="Dialog-title">
Welcome
</h1>
<p className="Dialog-message">
Thank you for visiting our spacecraft!
</p>
</FancyBorder>
);
}