譯者:前端小智html
就像人們對更新移動應用程序和操做系統感到興奮同樣,開發人員也應該對更新框架感到興奮。不一樣框架的新版本具備新特性和開箱即用的技巧。react
下面是將現有應用程序從 React 15 遷移到 React 16 時應該考慮的一些好特性。git
想閱讀更多優質文章請猛戳GitHub博客,一年百來篇優質文章等着你!github
React 16 引入了錯誤邊界的新概念。編程
如今在React 16中,你們就能使用錯誤邊界功能,而不用一發生錯誤就解除整個程序掛載了。把錯誤邊界當作是一種相似於編程中try-catch語句的機制,只不過是由 React 組件來實現的。數組
錯誤邊界是一種React組件。它及其子組件造成一個樹型結構,能捕獲JavaScript中全部位置的錯誤,記錄下錯誤,而且還能顯示一個後備界面,避免讓用戶直接看到組件樹的崩潰信息。安全
這裏涉及到一種新的生命週期函數叫componentDidCatch(error, info)
。不管什麼樣的類組件,只要定義了這個函數,就成爲了一個錯誤邊界。框架
class ErrorBoundary extends React.Component {
constructor(props) {
super(props);
this.state = { hasError: false };
}
componentDidCatch(error, info) {
// Display fallback UI
this.setState({ hasError: true });
// 你還能夠將錯誤記錄到錯誤報告服務中
logErrorToMyService(error, info);
}
render() {
if (this.state.hasError) {
// 能夠渲染任何自定義回退界面
return <h1>Something went wrong.</h1>;
}
return this.props.children;
}
}
複製代碼
也能夠將其用做常規組件使用:less
<ErrorBoundary>
<MyWidget />
</ErrorBoundary>
複製代碼
componentDidCatch()
方法的工做原理相似於JavaScript catch{}
塊,但它適用於組件。只有類組件能夠是錯誤邊界。實際上,在大多數狀況下,你都但願聲明一次錯誤邊界組件,而後在整個應用程序中使用它。
請注意,錯誤邊界只會捕獲位於它們之下的組件中的錯誤。錯誤邊界沒法捕獲到自身的錯誤。若是錯誤邊界渲染錯誤消息失敗,錯誤將被傳播到上方最接近的錯誤邊界。這也相似於 JavaScript 中的 catch{}塊。
有了錯誤邊界,即便某個組件的結果有錯誤,整個React程序掛載也不會被解除。只有出錯的那個組件會顯示一個後備界面,而整個程序仍然徹底正常運行。
關於錯誤邊界更多的內容可查看官網。
如今,在渲染時能夠擺脫將組件包裝在 div
中。
你如今能夠從組件的 render 方法返回元素數組。與其餘數組同樣,你須要爲每一個元素添加一個鍵以免發出鍵警告:
render() {
// No need to wrap list items in an extra element!
return [
// Don't forget the keys :)
<li key="A">First item</li>,
<li key="B">Second item</li>,
<li key="C">Third item</li>,
];
}
複製代碼
從React 16.2.0開始,它支持JSX的一個特殊片斷語法,該語法不須要鍵。
render() {
return (
<>
<ChildA />
<ChildB />
<ChildC />
</>
);
}
複製代碼
支持返回字符串:
render() {
return 'Look ma, no spans!';
}
複製代碼
Portal 提供了一種將子節點渲染到父節點以外的 dom 節點。
ReactDOM.createPortal(child, container)
複製代碼
第一個參數 (child
)是任何可渲染的 React子元素,例如元素,字符串或片斷。 第二個參數 (container)
是 DOM 元素。
在 React15.X 版本中,咱們只能講子節點在父節點中渲染,基本用法以下:
render() {
// React須要建立一個新的div來包含子節點
return (
<div>
{this.props.children}
</div>
);
}
複製代碼
可是若是須要將子節點插入到父節點以外的dom呢,React15.x 及以前都沒有提供這個功能的 API。 可使用 React16.0 中的 portal:
render() {
// React不須要建立一個新的div去包含子元素,直接將子元素渲染到另外一個
//dom節點中
//這個dom節點能夠是任何有效的dom節點,不管其所處於dom樹中的哪一個位置
return ReactDOM.createPortal(
this.props.children,
domNode,
);
}
複製代碼
Portal 的一個典型用例是這樣的:當父組件帶有 overflow:hidden
或 z-index
樣式時,你但願子組件在視覺上可以「突破」它的容器。例如,對話框、懸停卡和工具提示。
React15 會忽略任何未知的 DOM 屬性。React 會跳過它們,由於沒法識別它們。
// 你的代碼
<div mycustomattribute="something" />
複製代碼
React 15 將渲染一個空的 div:
// React 15 output:
<div />
複製代碼
在 React16 中,輸出將以下所示(會顯示自定義屬性,而且徹底不會被忽略)
// React 16 output:
<div mycustomattribute="something" />
複製代碼
有時候咱們須要經過函數來判斷組件狀態更新是否觸發從新渲染,在 React 16 中,咱們能夠經過調用 setState
時傳入 null
來避免組件從新渲染,這也就意味着,咱們能夠在 setState 方法內部決定咱們的狀態是否須要更新,
const MAX_PIZZAS = 20;
function addAnotherPizza(state, props) {
// Stop updates and re-renders if I've had enough pizzas.
if (state.pizza === MAX_PIZZAS) {
return null;
}
// If not, keep the pizzas coming! :D
return {
pizza: state.pizza + 1,
}
}
this.setState(addAnotherPizza);
複製代碼
更多相關信息請閱讀這裏
如今使用 React16 建立refs
要容易得多。 爲何須要使用refs
:
管理焦點、文本選擇或媒體播放。
觸發動畫。
與第三方 DOM 庫集成。
ref
是使用 React.createRef()
建立的,並經過 ref
屬性附加到 React 元素。ref
一般是在構造組件時被分配給實例的屬性,以便在整個組件中引用它們。
class MyComponent extends React.Component {
constructor(props) {
super(props);
this.myRef = React.createRef();
}
render () {
return <div ref={this.myRef}></div>
}
}
複製代碼
上述是建立Ref
指向的方法, 在Ref 所指向的組件,在render
後就能夠調用,React16.3 中提供了current
屬性,用於引用render
後的節點:
componentDidMount(){
console.log(this.myRef.current);
//render以後就能夠輸出該ref指向的那個節點
}
複製代碼
此外,一樣的 Ref 所指向的節點能夠是 dom 節點,也能夠是類組件。
Ref 的值因節點的類型不一樣而有所不一樣:
當 ref 屬性用於 HTML 元素時,在構造函數中使用 React.createRef() 建立的 ref 將底層 DOM 元素做爲 current 屬性。
當 ref 屬性用於自定義類組件時,ref 對象將已掛載的組件實例做爲 current 屬性。
你可能不會在函數組件上使用 ref 屬性,由於它們沒有實例。
Context 提供了一種經過組件樹傳遞數據的方法,無需在每一層手動傳遞 prop
。
const { Provider, Consumer } = React.createContext(defaultValue)
複製代碼
建立{Provider,Consumer}
對。當 React 渲染 Consumer 時,它將從樹中最接近的 Provider 讀取當前上下文值。
defaultValue
參數只在消費者在樹中找不到匹配的 Provider 時纔會用到,這在單獨測試組件時十分有用。注意:將 undefined
做爲 Provider 值傳遞進去並不會致使 Consumer 使用 defaultValue
。
<Provider value={/* some value */}>
複製代碼
一個容許 Consumer 訂閱上下文變動的 React 組件。
一個 Provider 能夠鏈接多個 Consumer,能夠在樹中嵌套 Provider,實現更深的值覆蓋。
<Consumer>
{value => /* render something based on the context value */}
</Consumer>
複製代碼
訂閱上下文更改的 React 組件。
須要一個函數做爲子組件。這個函數接收當前上下文值,並返回一個 React 節點。傳給函數的 value 參數將等於樹中最近的 Provider
的 value
。若是沒有匹配的 Provider,則 value
參數將等於傳給 createContext()
的 defaultValue。
在很長一段時間內,componentWillReceiveProps
是在沒有附加渲染的狀況下更新狀態的惟一方法。
在版本16.3中,咱們引入了一個全新的生命週期函數getDerivedStateFromProps
用來替換componentWillReceiveProps
,並用更安全的方式處理相同的場景。
與此同時,咱們意識到人們對如何使用這兩種方法有不少誤解,咱們發現了一些反模式,這些錯誤致使了微妙而使人困惑的bug。
在16.4中,有關getDerivedStateFromProps
的修復使得派生狀態更加可預測,所以錯誤使用的結果更容易被注意到。
getDerivedStateFromProps
會在調用 render
方法以前被調用,它應該返回一個用於更新狀態的對象,或者若是不更新任何狀態就返回 null
。
這個方法適用於一些罕見的用例,其中 state
依賴 prop
的變化。例如,能夠很方便地實現一個<Transition>
組件,它會比較上一個和下一個子組件,而後決定它們中的哪一個須要進行動畫渲染。
衍生 state 會致使冗長的代碼,並讓你的組件難以開發和維護。
你能夠考慮更簡單的替代方案:
若是你須要在 prop 發生變動時作一些其餘事情(例如數據提取或動畫),請改用 componentDidUpdate
生命週期。
若是你只想在 prop 發生變動時從新計算某些數據,請改用 memoization helper:
* 若是你想在 prop 發生變動時「重置」某個狀態,請考慮建立受控組件或帶有鍵的非受控組件。
props
的純函數和類定義以外的狀態,在getDerivedStateFromProps()
和其餘類方法之間重用一些代碼。注意,無論怎樣,這個方法都會在每次進行渲染時觸發。這與 UNSAFE_componentWillReceiveProps
徹底相反。它只在父組件進行從新渲染時觸發,並且不做爲本地 setState
的結果。
將nextProps.someValue
與this.props.someValue進行比較。 若是二者都不一樣,那麼咱們執行一些操做:
static getDerivedStateFromProps(nextProps, prevState){
if(nextProps.someValue!==prevState.someValue){
return { someState: nextProps.someValue};
}
return null
}
複製代碼
它接收兩個參數 nextProps
和 prevState
。如前所述,你沒法在這個方法中訪問 this
。你必須將 prop 存儲在 state 中,而後將 nextProps
與以前的 prop 進行對比。在上面的代碼中,nextProps 和 prevState 進行了比較。若是二者不一樣,則返回一個用於更新狀態的對象,不然就返回 null,表示不須要更新狀態。若是 state 發生變動,就會調用 componentDidUpdate,咱們能夠像在 componentWillReceiveProps 中那樣執行所需的操做。
react v16.3,最大的變更莫過於生命週期去掉了如下三個:
同時爲了彌補失去上面三個週期的不足又加了兩個:
static getDerivedStateFromProps
getSnapshotBeforeUpdate
舊的生命週期十分完整,基本能夠捕捉到組件更新的每個state/props/ref,沒有什從邏輯上的毛病。
可是架不住官方本身搞事情,react打算在17版本推出新的Async Rendering,提出一種可被打斷的生命週期,而能夠被打斷的階段正是實際dom掛載以前的虛擬dom構建階段,也就是要被去掉的三個生命週期。
生命週期一旦被打斷,下次恢復的時候又會再跑一次以前的生命週期,所以componentWillMount,componentWillReceiveProps, componentWillUpdate 都不能保證只在掛載/拿到props/狀態變化的時候刷新一次了,因此這三個方法被標記爲不安全。
你的點贊是我持續分享好東西的動力,歡迎點贊!
乾貨系列文章彙總以下,以爲不錯點個Star,歡迎 加羣 互相學習。
我是小智,公衆號「大遷世界」做者,對前端技術保持學習愛好者。我會常常分享本身所學所看的乾貨,在進階的路上,共勉!
關注公衆號,後臺回覆福利,便可看到福利,你懂的。