React
React 是 React 庫的頂層 API,可以使用以下幾個(gè)方式引入
使用 script 標(biāo)簽引入的組件庫,全局有一個(gè) React 的對(duì)象
使用 ES6 和 npm 的時(shí)候,可以通過編寫
import React from 'react'來進(jìn)行引入ES5 與 npm 時(shí),則可以通過編寫
var React = require('react')來引入它們
React.memo
const MyComponent = React.memo(function MyComponent(props) {
/* 使用 props 渲染 */
});
如果你的組件在相同 props 的情況下渲染相同的結(jié)果,那么你可以通過將其包裝在 React.memo 中調(diào)用
React.memo 僅檢查 props 變更。
如果函數(shù)組件被 React.memo 包裹,且其實(shí)現(xiàn)中擁有 useState,useReducer 或 useContext 的 Hook,當(dāng) state 或 context 發(fā)生變化時(shí),它仍會(huì)重新渲染。
默認(rèn)情況下其只會(huì)對(duì)復(fù)雜對(duì)象做淺層對(duì)比,如果你想要控制對(duì)比過程,那么請將自定義的比較函數(shù)通過第二個(gè)參數(shù)傳入來實(shí)現(xiàn)。
function MyComponent(props) {
/* 使用 props 渲染 */
}
function areEqual(prevProps, nextProps) {
/*
如果把 nextProps 傳入 render 方法的返回結(jié)果與
將 prevProps 傳入 render 方法的返回結(jié)果一致則返回 true,
否則返回 false
*/
}
export default React.memo(MyComponent, areEqual);
React.Children
React.Children 提供了用于處理 this.props.children 不透明數(shù)據(jù)結(jié)構(gòu)的實(shí)用方法。
React.forwardRef
React.forwardRef 會(huì)創(chuàng)建一個(gè) React 組件,這個(gè)組件能夠?qū)⑵浣邮艿?ref 屬性轉(zhuǎn)發(fā)到其組件樹下的另一個(gè)組件中。
在以下兩種場景中特別有用:
轉(zhuǎn)發(fā) refs 到 DOM 組件
在高階組件中轉(zhuǎn)發(fā) refs
React.forwardRef 接受渲染函數(shù)作為參數(shù)。React 將使用 props 和 ref 作為參數(shù)來調(diào)用此函數(shù)。此函數(shù)應(yīng)返回 React 節(jié)點(diǎn)。
const FancyButton = React.forwardRef((props, ref) => (
<button ref={ref} className="FancyButton">
{props.children}
</button>
));
// You can now get a ref directly to the DOM button:
const ref = React.createRef();
<FancyButton ref={ref}>Click me!</FancyButton>;
React.Component
class 組件中必須定義 render 方法,其他都是可選的
常用的生命周期方法
render()
render() 函數(shù)應(yīng)該為純函數(shù),這意味著在不修改組件 state 的情況下,每次調(diào)用時(shí)都返回相同的結(jié)果,并且它不會(huì)直接與瀏覽器交互。
如需與瀏覽器進(jìn)行交互,請?jiān)?componentDidMount() 或其他生命周期方法中執(zhí)行你的操作。
constuctor(props)
如果不初始化 state 或不進(jìn)行方法綁定,則不需要為 React 組件實(shí)現(xiàn)構(gòu)造函數(shù)。
在 constructor() 函數(shù)中不要調(diào)用 setState() 方法。如果你的組件需要使用內(nèi)部 state,請直接在構(gòu)造函數(shù)中為 this.state 賦值初始 state:
componentDidMount()
componentDidMount() 會(huì)在組件掛載后(插入 DOM 樹中)立即調(diào)用。依賴于 DOM 節(jié)點(diǎn)的初始化應(yīng)該放在這里。如需通過網(wǎng)絡(luò)請求獲取數(shù)據(jù),此處是實(shí)例化請求的好地方。
你可以在 componentDidMount() 里直接調(diào)用 setState()。它將觸發(fā)額外渲染,但此渲染會(huì)發(fā)生在瀏覽器更新屏幕之前。如此保證了即使在 render() 兩次調(diào)用的情況下,用戶也不會(huì)看到中間狀態(tài)。
componentDidUpdate(prevProps, prevState, snapshot)
你也可以在 componentDidUpdate() 中直接調(diào)用 setState(),但請注意它必須被包裹在一個(gè)條件語句里,否則會(huì)導(dǎo)致死循環(huán)。
它還會(huì)導(dǎo)致額外的重新渲染,雖然用戶不可見,但會(huì)影響組件性能。 變化更新到 DOM 但是渲染到頁面之前
componentWillUnmount()
componentWillUnmount() 會(huì)在組件卸載及銷毀之前直接調(diào)用。在此方法中執(zhí)行必要的清理操作
不常用的生命周期方法
shouldComponentUpdate(nextProps, nextState)
根據(jù) shouldComponentUpdate() 的返回值,判斷 React 組件的輸出是否受當(dāng)前 state 或 props 更改的影響。默認(rèn)行為是 state 每次發(fā)生變化組件都會(huì)重新渲染。
true - 重新渲染
false - 不重新渲染(會(huì)跳過當(dāng)前組件以及其子組件的渲染,類似于 Angular 的 ChangeDetectionStrategy.OnPush)
如果你需要手動(dòng)編寫此函數(shù),可以將 this.props 與 nextProps 以及 this.state 與 nextState 進(jìn)行比較,并返回 false 以告知 React 可以跳過更新。返回 false 并不會(huì)阻止子組件在 state 更改時(shí)重新渲染。
static getDerivedStateFromProps(props, state)
會(huì)在 shouldComponentUpdate 之前調(diào)用。返回一個(gè)對(duì)象來更新 state,如果返回 null 則不更新任何內(nèi)容。
此方法無權(quán)訪問組件實(shí)例
getSnapshotBeforeUpdate(prevProps, prevState)
在 render 之后,componentDidUpdate 之前調(diào)用
Error Boundary
static getDerivedStateFromError(error)
此生命周期會(huì)在后代組件拋出錯(cuò)誤后被調(diào)用。 它將拋出的錯(cuò)誤作為參數(shù),并返回一個(gè)值以更新 state
getDerivedStateFromError() 會(huì)在渲染階段調(diào)用,因此不允許出現(xiàn)副作用。 如遇此類情況,請改用 componentDidCatch()。
componentDidCatch(error, info)
componentDidCatch() 會(huì)在“提交”階段被調(diào)用,因此允許執(zhí)行副作用。
其他 API
setState(updater, [callback])
setState() 將對(duì)組件 state 的更改排入隊(duì)列,并通知 React 需要使用更新后的 state 重新渲染此組件及其子組件。
React 會(huì)延遲調(diào)用它,然后通過一次傳遞更新多個(gè)組件。React 并不會(huì)保證 state 的變更會(huì)立即生效。
setState() 并不總是立即更新組件。它會(huì)批量推遲更新。
請使用 componentDidUpdate 或者 setState 的回調(diào)函數(shù)(setState(updater, callback))來獲取最新的 state,這兩種方式都可以保證在應(yīng)用更新后觸發(fā)。
基于之前的 state 來設(shè)置當(dāng)前的 state,可以給 setState 的第一個(gè)參數(shù)傳遞一個(gè)函數(shù),函數(shù)有兩個(gè)參數(shù)(state 和 props)都代表最新的 state 和 props。updater 函數(shù)中接收的 state 和 props 都保證為最新。updater 的返回值會(huì)與 state 進(jìn)行淺合并。
React 會(huì)按照調(diào)用 setState 的順序更新狀態(tài)
無論您在 React 事件處理程序 setState()中調(diào)用多少組件,它們都只會(huì)在事件結(jié)束時(shí)產(chǎn)生一次重新渲染。
class Container extends React.Component {
constructor(props) {
super(props);
this.state = { a: false, b: false };
}
render() {
return (
<button onClick={this.handleClick}>
{this.state.a.toString() + " " + this.state.b.toString()}
</button>
);
}
handleClick = () => {
// 點(diǎn)擊按鈕之后a、b都是true
this.setState({ a: true });
this.setState({ b: true });
};
}
解釋一下:
setState()調(diào)用都發(fā)生在 React 事件處理程序中。因此,它們總是在事件結(jié)束時(shí)一起刷新(并且您看不到中間狀態(tài))
更新總是按照它們發(fā)生的順序進(jìn)行淺層合并。所以如果第一次更新是{a: 10},第二次是{b: 20},第三次是{a: 30},渲染狀態(tài)將是{a: 30, b: 20}。對(duì)同一狀態(tài)鍵的最新更新(例如 a 在我的示例中)總是“獲勝”。
但是在下面的這種情況中,當(dāng)一個(gè)新的 state 依賴于之前的 state 的時(shí)候,結(jié)果可能與我們想象的不一致
// assuming this.state = { value: 0 };
this.setState({ value: this.state.value + 1 });
this.setState({ value: this.state.value + 1 });
this.setState({ value: this.state.value + 1 });
// 最后 this.state.value 為 1
此時(shí)推薦使用韓式
// assuming this.state = { value: 0 };
this.setState(state => ({ value: state.value + 1 }));
this.setState(state => ({ value: state.value + 1 }));
this.setState(state => ({ value: state.value + 1 }));
// 最后 this.state.value 為 3
- 何時(shí)以及為什么 setState 會(huì)批量執(zhí)行?
何時(shí):setState 的異步更新是在 React 的生命周期函數(shù)以及 React 的事件處理函數(shù)中,除此之外的場景(定時(shí)器、ajax 請求、Promise 中、addEventListener 綁定的事件處理函數(shù)中都是同步執(zhí)行的)
為什么:優(yōu)化性能
實(shí)例屬性
state
組件中的 state 包含了隨時(shí)可能發(fā)生變化的數(shù)據(jù)。state 由用戶自定義,它是一個(gè)普通 JavaScript 對(duì)象。
如果某些值未用于渲染或數(shù)據(jù)流(例如,計(jì)時(shí)器 ID),則不必將其設(shè)置為 state。此類值可以在組件實(shí)例上定義。
永遠(yuǎn)不要直接改變 this.state,因?yàn)楹罄m(xù)調(diào)用的 setState() 可能會(huì)替換掉你的改變。請把 this.state 看作是不可變的。
ReactDOM
script 標(biāo)簽引入的,全局有一個(gè) ReactDOM
ES6 和 npm,通過
import ReactDOM form 'react-dom'來使用 ReactDOM使用 npm 和 ES5,你可以用
var ReactDOM = require('react-dom')。
ReactDOM.render(element, container[, callback])
在提供的 container 里渲染一個(gè) React 元素,并返回對(duì)該組件的引用
ReactDOM.render() 不會(huì)修改容器節(jié)點(diǎn)(只會(huì)修改容器的子節(jié)點(diǎn))。可以在不覆蓋現(xiàn)有子節(jié)點(diǎn)的情況下,將組件插入已有的 DOM 節(jié)點(diǎn)中。
ReactDOM.unmountComponentAtNode(container)
從 DOM 中卸載組件,會(huì)將其事件處理器(event handlers)和 state 一并清除。如果指定容器上沒有對(duì)應(yīng)已掛載的組件,這個(gè)函數(shù)什么也不會(huì)做。如果組件被移除將會(huì)返回 true,如果沒有組件可被移除將會(huì)返回 false。
DOM 元素
React 實(shí)現(xiàn)了一套獨(dú)立于瀏覽器的 DOM 系統(tǒng),兼顧了性能和跨瀏覽器的兼容性。
屬性差異
checked
當(dāng) <input> 組件的 type 類型為 checkbox 或 radio 時(shí),組件支持 checked 屬性。你可以使用它來設(shè)置組件是否被選中。這對(duì)于構(gòu)建受控組件很有幫助。而 defaultChecked 則是非受控組件的屬性,用于設(shè)置組件首次掛載時(shí)是否被選中。
className
className 屬性用于指定 CSS 的 class
React 中使用 Web Components(這是一種不常見的使用方式),請使用 class 屬性代替。
dangerouslySetInnerHTML
類似于 HTML 中的 innerHTML 屬性
可以直接在 React 中設(shè)置 HTML,但當(dāng)你想設(shè)置 dangerouslySetInnerHTML 時(shí),需要向其傳遞包含 key 為 __html 的對(duì)象,以此來警示你
htmlFor
由于 for 在 JavaScript 中是保留字,所以 React 元素中使用了 htmlFor 來代替。
onChange
onChange 事件與預(yù)期行為一致:每當(dāng)表單字段變化時(shí),該事件都會(huì)被觸發(fā)。
selected
如果要將 <option> 標(biāo)記為已選中狀態(tài),請?jiān)?select 的 value 中引用該選項(xiàng)的值。
style
style 接受一個(gè)采用小駝峰命名屬性的 JavaScript 對(duì)象,而不是 CSS 字符串。
const divStyle = {
color: "blue",
backgroundImage: "url(" + imgUrl + ")"
};
function HelloWorldComponent() {
return <div style={divStyle}>Hello World!</div>;
}
React 會(huì)自動(dòng)添加 ”px” 后綴到內(nèi)聯(lián)樣式為數(shù)字的屬性后。如需使用 ”px” 以外的單位,請將此值設(shè)為數(shù)字與所需單位組成的字符串。
value
<input>、<select> 和 <textarea> 組件支持 value 屬性。你可以使用它為組件設(shè)置 value。這對(duì)于構(gòu)建受控組件是非常有幫助。defaultValue 屬性對(duì)應(yīng)的是非受控組件的屬性,用于設(shè)置組件第一次掛載時(shí)的 value。
合成事件
SyntheticEvent 實(shí)例將被傳遞給你的事件處理函數(shù),它是瀏覽器的原生事件的跨瀏覽器包裝器。除兼容所有瀏覽器外,它還擁有和瀏覽器原生事件相同的接口,包括 stopPropagation() 和 preventDefault()。
使用 nativeEvent 屬性來獲取瀏覽器的底層事件。
事件處理函數(shù)在冒泡階段被觸發(fā)。如需注冊捕獲階段的事件處理函數(shù),則應(yīng)為事件名添加 Capture。例如,處理捕獲階段的點(diǎn)擊事件請使用 onClickCapture,而不是 onClick。