做者 | Daishi Kato
譯者 | 王文剛
編輯 | Yoniereact
React useContext 使用起來很是方便,它能夠訪問定義 DOM 樹中多個組件的全局狀態或共享狀態。
可是,useContext 不是專爲全局狀態設計的,而且有一個警告:對上下文值的任何更改都會多播,致使全部 useContext 從新渲染組件。
這篇文章展現了一些關於問題的示例代碼以及具備 state 使用跟蹤的解決方案。git
咱們以人爲對象來舉例說明。github
const initialState = { firstName: 'Harry', familyName: 'Potter', };
咱們使用上下文和 local state。redux
const PersonContext = createContext(null); const PersonProvider = ({ children }) => { const [person, setPerson] = useState(initialState); return ( <PersonContext.Provider value={[person, setPerson]}> {children} </PersonContext.Provider> ); };
最後,這是一個顯示人物名字的組件。ide
const DisplayFirstName = () => { const [person] = useContext(PersonContext); return ( <div>First Name: {person.firstName}</div> ); };
到如今爲止還挺好。
可是,問題是當你更新此人的 family name 的同時,它將觸發 DisplayFirstName 從新渲染,甚至渲染結果都是相同的。
請注意,這是否是一個真正的問題,直到它成爲一個問題。一般狀況下,大多數較小的應用程序均可以正常運行,可是一些較大的應用程序會產生性能問題。性能
讓咱們看看狀態使用跟蹤如何解決這個問題。
provider 看起來有點不一樣,但代碼基本相同。優化
const usePerson = () => useState(initialState); const { Provider, useTracked } = createContainer(usePerson); const PersonProvider = ({ children }) => ( <Provider> {children} </Provider> );
DisplayFirstName 組件像這樣更改。spa
const DisplayFirstName = () => { const [person] = useTracked(); return ( <div>First Name: {person.firstName}</div> ); };
注意這個變化?只有區別是 useTracked()而不是 useContext(...)。
經過這個小的更改,跟蹤 DisplayFirstName 中的狀態使用狀況。如今,即便更新了 family name,只要 first name 未更新,該組件就不會從新渲染。
這是輕鬆的渲染優化。設計
有些讀者可能會認爲也能夠經過相似 useSelector 的 hooks API 來實現。
這是另外一個使用 useTracked 的例子。code
const initialState = { firstName: 'Harry', familyName: 'Potter', showFullName: false, };
假設咱們有一個像上面這樣的 state ,讓咱們建立一個帶有條件的組件。
const DisplayPersonName = () => { const [person] = useTracked(); return ( <div> {person.showFullName ? ( <span> Full Name: {person.firstName} <Divider /> {person.familyName} </span> ) : ( <span>First Name: {person.firstName}</span> )} </div> ); };
此組件將在兩個場景中從新渲染:
a)當更新 firstName 或 familyName 時,顯示全名。
b)當更新 firstName 時,不顯示全名。
使用 useSelector 重現相同的行爲並不容易,最終可能會在組件中分開。
有兩個項目使用狀態來跟蹤:
https://github.com/dai-shi/reactive-react-redux
這篇文章重點介紹瞭如何輕鬆使用狀態跟蹤。篇幅限制,咱們並無討論這些類庫的具體實現。簡而言之,咱們使用 Proxy API 來跟蹤狀態使用狀況。咱們還在 Context API 中使用未記錄的功能來中止廣播。若是你對這些細節感興趣,請查看如上所述的 GitHub 庫。