React 新特性
-
React
中一個常見模式是為一個元件返回多個元素。Fragments
可以讓你聚合一個子元素列表,並且不在DOM
中增加額外節點。
class Columns extends React.Component {
render() {
return (
<React.Fragment>
<td>Hello</td>
<td>World</td>
</React.Fragment>
);
}
}
注意:①key
是唯一可以傳遞給 Fragment
的屬性。② 在 React
中,<></>
是 <React.Fragment><React.Fragment/>
的語法糖。
-
Strict Mode
嚴格模式StrictMode
是一個用以標記出應用中潛在問題的工具。與Fragment
類似,StrictMode
不會渲染任何真實的UI
。它為其後代元素觸發額外的檢查和警告。
import { StrictMode, Component } from 'react'
class Child extends Component {
// 以下三個函式在 React v16.3 已不被推薦,未來的版本會廢棄。
componentWillMount() {
console.log('componentWillMount')
}
componentWillUpdate() {
console.log('componentWillUpdate')
}
componentWillReceiveProps() {
console.log('componentWillReceiveProps')
}
render() {
return (
<div />
)
}
}
export default class StrictModeExample extends Component {
render() {
return (
<StrictMode>
<Child />
</StrictMode>
)
}
}
由於在StrictMode
內使用了三個即將廢棄的API
,開啟控制檯 ,可看到如下錯誤提醒:
註釋:嚴格模式檢查只在開發模式下執行,不會與生產模式衝突。
-
createRef (v16.3)
(1) 老版本ref
使用方式
① 字串形式:<input ref="input" />
② 回撥函式形式:<input ref={input => (this.input = input)} />
(2) 字串形式缺點
① 需要內部追蹤ref
的this
取值,會使React
稍稍變慢。
② 有時候this
與你想象的並不一致。
import React from 'react'
class Children extends React.Component {
componentDidMount() {
// <h1></h1>
console.log('children ref', this.refs.titleRef)
}
render() {
return (
<div>
{this.props.renderTitle()}
</div>
)
}
}
class Parent extends React.Component {
// 放入子元件渲染
renderTitle = () => (
<h1 ref='titleRef'>{this.props.title}</h1>
)
componentDidMount() {
// undefined
console.log('parent ref:', this.refs.titleRef)
}
render() {
return (
<Children renderTitle={this.renderTitle}></Children>
)
}
}
export default Parent
(3) createRef
語法
import React from 'react'
class MyComponent extends React.Component {
constructor(props) {
super(props);
this.inputRef = React.createRef();
}
render() {
return <input type="text" ref={this.inputRef} />;
}
componentDidMount() {
this.inputRef.current.focus();
}
}
export default MyComponent
- 呼叫
setState
更新狀態時,若之後的狀態依賴於之前的狀態,推薦使用傳入函式形式。
語法:setState((prevState, props) => stateChange, [callback])
例如,假設我們想通過props.step
在狀態中增加一個值:
this.setState((prevState, props) => {
return {counter: prevState.counter + props.step};
});
- 錯誤邊界
(1) 錯誤邊界用於捕獲其子元件樹JavaScript
異常,記錄錯誤並展示一個回退的UI
的React
元件,避免整個元件樹異常導致頁面空白。
(2) 錯誤邊界在渲染期間、生命週期方法內、以及整個元件樹建構函式內捕獲錯誤。
(3) 元件如果定義了static getDerivedStateFromError()
或componentDidCatch()
中的任意一個或兩個生命週期方法 。當其子元件丟擲錯誤時,可使用static getDerivedStateFromError()
更新state
,可使用componentDidCatch()
記錄錯誤資訊。
class ErrorBoundary extends React.Component {
constructor(props) {
super(props);
this.state = { hasError: false };
}
static getDerivedStateFromError(error) {
// Update state so the next render will show the fallback UI.
return { hasError: true };
}
componentDidCatch(error, info) {
// You can also log the error to an error reporting service
logErrorToMyService(error, info);
}
render() {
if (this.state.hasError) {
// You can render any custom fallback UI
return <h1>Something went wrong.</h1>;
}
return this.props.children;
}
}
而後你可以像一個普通的元件一樣使用:
<ErrorBoundary>
<MyWidget />
</ErrorBoundary>
註釋:getDerivedStateFromError
和componentDidCatch
方法使用一個即可捕獲子元件樹錯誤。
-
Portal
(1)Portals
提供了一種很好的將子節點渲染到父元件以外的DOM
節點的方式。
(2) 通過Portals
進行事件冒泡
儘管portal
可以被放置在DOM
樹的任何地方,但在其他方面其行為和普通的React
子節點行為一致。一個從portal
內部會觸發的事件會一直冒泡至包含React
樹 的祖先。
import React from 'react';
import ReactDOM from 'react-dom';
class Modal extends React.Component {
render() {
return this.props.clicks % 2 === 1
? this.props.children
: ReactDOM.createPortal(
this.props.children,
document.getElementById('modal'),
);
}
}
class Child extends React.Component {
render() {
return (
<button>Click</button>
);
}
}
class Parent extends React.Component {
constructor(props) {
super(props);
this.state = {clicks: 0};
this.handleClick = this.handleClick.bind(this);
}
handleClick() {
this.setState(prevState => ({
clicks: prevState.clicks + 1
}));
}
render() {
return (
<div onClick={this.handleClick}>
<p>Number of clicks: {this.state.clicks}</p>
<Modal clicks={this.state.clicks}>
<Child />
</Modal>
</div>
);
}
}
export default Parent
註釋:當元件由portal
渲染方式切換為普通渲染方式,會導致該元件被解除安裝之後重新渲染。元件放大功能如果通過portal
方式實現,放大前的狀態(滾動位置、焦點位置等)無法保持。
-
fiber
介紹fiber
是React 16
中新的和解引擎。它的主要目的是使虛擬DOM
能夠進行增量渲染。
(1) 同步更新過程的侷限
當React
決定要載入或者更新元件樹時,會做很多事,比如呼叫各個元件的生命週期函式,計算和比對Virtual DOM
,最後更新DOM
樹,這整個過程是同步進行的。瀏覽器那個唯一的主執行緒都在專心執行更新操作,無暇去做任何其他的事情。
(2)React Fiber
的方式
破解JavaScript
中同步操作時間過長的方法其實很簡單——分片。React Fiber
把更新過程碎片化,執行過程如下面的圖所示,每執行完一段更新過程,就把控制權交還給React
負責任務協調的模組,看看有沒有其他緊急任務要做,如果沒有就繼續去更新,如果有緊急任務,那就去做緊急任務。
維護每一個分片的資料結構,就是Fiber
。
(3)React Fiber
更新過程的兩個階段
在React Fiber
中,一次更新過程會分成多個分片完成,所以完全有可能一個更新任務還沒有完成,就被另一個更高優先順序的更新過程打斷,這時候,優先順序高的更新任務會優先處理完,而低優先順序更新任務所做的工作則會完全作廢,然後等待機會重頭再來。React Fiber
一個更新過程被分為兩個階段(Phase
):第一個階段Reconciliation Phase
和第二階段Commit Phase
。
在第一階段Reconciliation Phase
,React Fiber
會找出需要更新哪些DOM
,這個階段是可以被打斷的;但是到了第二階段Commit Phase
,那就一鼓作氣把DOM
更新完,絕不會被打斷。
(4)React Fiber
對現有程式碼的影響
因為第一階段的過程會被打斷而且“重頭再來”,就會造成第一階段中的生命週期函式在一次載入和更新過程中可能會被多次呼叫!
第一個階段的四個生命週期函式中,componentWillReceiveProps
、componentWillMount
和componentWillUpdate
這三個函式可能包含副作用,所以當使用React Fiber
的時候一定要重點看這三個函式的實現。
註釋:大家應該都清楚程式(Process
)和執行緒(Thread
)的概念,在電腦科學中還有一個概念叫做Fiber
,英文含義就是“纖維”,意指比Thread
更細的線,也就是比執行緒(Thread
)控制得更精密的併發處理機制。 - 宣告週期變化
從v16.3
開始,原來的三個生命週期componentWillMount
、componentWillUpdate
、componentWillReceiveProps
將被廢棄,取而代之的是兩個全新的生命週期:
①static getDerivedStateFromProps
②getSnapshotBeforeUpdate
-
getDerivedStateFromProps
用法static getDerivedStateFromProps(nextProps, prevState)
元件例項化後和接受新屬性時將會呼叫getDerivedStateFromProps
。它應該返回一個物件來更新狀態,或者返回null
來表明新屬性不需要更新任何狀態。
如果父元件導致了元件的重新渲染,即使屬性沒有更新,這一方法也會被呼叫。
如果你只想處理變化,你可能想去比較新舊值。呼叫this.setState()
通常不會觸發getDerivedStateFromProps()
。 -
getStapshotBeforeUpdate
getSnapshotBeforeUpdate()
在最新的渲染輸出提交給DOM
前將會立即呼叫。它讓你的元件能在當前的值可能要改變前獲得它們。這一生命週期返回的任何值將會 作為引數被傳遞給componentDidUpdate()
。
class ScrollingList extends React.Component {
listRef = React.createRef();
getSnapshotBeforeUpdate(prevProps, prevState) {
// Are we adding new items to the list?
// Capture the current height of the list so we can adjust scroll later.
if (prevProps.list.length < this.props.list.length) {
return this.listRef.current.scrollHeight;
}
return null;
}
componentDidUpdate(prevProps, prevState, snapshot) {
// If we have a snapshot value, we've just added new items.
// Adjust scroll so these new items don't push the old ones out of view.
if (snapshot !== null) {
this.listRef.current.scrollTop +=
this.listRef.current.scrollHeight - snapshot;
}
}
render() {
return (
<div ref={this.listRef}>{/* ...contents... */}</div>
);
}
}
在上面的例子中,為了支援非同步渲染,在getSnapshotBeforeUpdate
中讀取scrollHeight
而不是componentWillUpdate
,這點很重要。由於非同步渲染,在“渲染”時期(如componentWillUpdate
和render
)和“提交”時期(如getSnapshotBeforeUpdate
和componentDidUpdate
)間可能會存在延遲。如果一個使用者在這期間做了像改變瀏覽器尺寸的事,從componentWillUpdate
中讀出的scrollHeight
值將是滯後的。
-
Context
用法
import React from 'react'
const ThemeContext = React.createContext();
const ThemedButton = (props) => (
<ThemeContext.Consumer>
{ context => <span style={{color: context}}>{props.text}</span> }
</ThemeContext.Consumer>
)
const Toolbar = () => (
<ThemeContext.Provider value='red'>
<ThemedButton text="Context API"/>
</ThemeContext.Provider>
)
export default Toolbar
註釋:在 Context.Consumer
中,children
必須為函式。
參考資料
相關文章
- 【react】React 18新特性React
- React16 新特性React
- React Native 0.59 新特性React Native
- React.JS 18新特性ReactJS
- React 16.4新特性發布React
- [譯]React v16.9 新特性React
- React 16 新特性全解(上)React
- React 16 新特性使用總結React
- React v16.6 新特性介紹React
- 深入React v16新特性(二)React
- React 16.0+ 新特性初探(How to use)React
- 深入React v16新特性(一)React
- React Native 0.59.x新特性解讀React Native
- react-router v6新特性總結React
- 一起學習React18 新特性React
- 利用React 16.6新特性優化應用效能React優化
- 30分鐘精通React今年最勁爆的新特性——React HooksReactHook
- react 16.8版本新特性以及對react開發的影響React
- QQ音樂:React v16 新特性實踐React
- 從實際開發的角度去看react的新特性hooksReactHook
- React Hooks 鉤子特性ReactHook
- 新特性
- React-Router v6 新特性解讀及遷移指南React
- Hadoop新特性Hadoop
- 新特性介面
- React 特性剪輯(版本 16.0 ~ 16.9)React
- IDL 9.1新特性
- .NET 7新特性
- ES 2024 新特性
- Java 8 新特性Java
- Prometheus 2.21.0 新特性Prometheus
- Go 1.13 新特性Go
- Servlet 3.0 新特性Servlet
- MySQL 8.0 新特性MySql
- PHP 7.4 新特性PHP
- Java 17新特性Java
- PostgreSQL 13–新特性SQL
- C++新特性C++