您好,登錄后才能下訂單哦!
這篇文章主要介紹“ReactQuery渲染優化的方法是什么”的相關知識,小編通過實際案例向大家展示操作過程,操作方法簡單快捷,實用性強,希望這篇“ReactQuery渲染優化的方法是什么”文章能幫助大家解決問題。
在之前的例子中我說過,下面這個組件只會在todos的length變化時才會重新渲染,其實我只說了一部分事實:
export const useTodosQuery = (select) => useQuery(['todos'], fetchTodos, { select }) export const useTodosCount = () => useTodosQuery((data) => data.length) function TodosCount() { const todosCount = useTodosCount() return <div>{todosCount.data}</div> }
每次發生后臺refetch的時候,這個組件都會下面的數據分別進行一次渲染:
{ status: 'success', data: 2, isFetching: true } { status: 'success', data: 2, isFetching: false }
這是因為React Query在每個查詢中返回了很多基本信息,isFetching
就是其中一個。這個屬性在請求正在發生的時候會被設置為true。這個在你想要展示一個后臺請求的loading標志的時候特別有用。但是如果你不需要,那確實會造成一些不必要的渲染。
對于上面說到的這個場景,React Query提供了notifyOnChangeProps
參數。他可以在每個場景單獨設置來告訴React Query:只在這些屬性發生變化的時候再通知我。通過將這個參數設置為['data']
,我們可以實現一個新的版本:
export const useTodosQuery = (select, notifyOnChangeProps) => useQuery(['todos'], fetchTodos, { select, notifyOnChangeProps }) export const useTodosCount = () => useTodosQuery((data) => data.length, ['data'])
盡管上面的代碼可以正常工作,但是它很容易就會造成不同步。如果我們希望針對error
進行特殊處理呢?又或者我們需要使用isLoading
屬性呢?我們不得不確保notifyOnChangeProps
屬性和我們實際用到的數據保持同步。如果我們忘記將某個數據添加到屬性里面,而只監聽data屬性的變化,當查詢返回錯誤,同時我們也要展示這些錯誤的時候,我們的組件并不會重新渲染。這個問題當我們把這些屬性寫死在自定義hook的時候格外明顯,因為我們并不知道使用自定義hook的組件實際上會用到哪些數據:
export const useTodosCount = () => useTodosQuery((data) => data.length, ['data']) function TodosCount() { // ???? we are using error, but we are not getting notified if error changes! const { error, data } = useTodosCount() return ( <div> {error ? error : null} {data ? data : null} </div> ) }
就像我在文章開頭免責聲明中說的,我認為這是比偶爾發生的不必要的重新渲染更壞的事情。當然,我們可以傳參數給自定義hook,但是這還是需要手動處理,是否有什么方式可以自動處理這個情況呢?請看:
這是我感受特別自豪的一個特性,這也是我對這個庫第一個重大的貢獻。如果你將notifyOnChangeProps
設置為'tracked'
,React Query會跟蹤你在渲染過程中用到的數據,會自動計算依賴列表。最終的效果就跟你手動維護這個列表一樣,除了你不用再去關注這個問題以外。你也可以全局開啟這個特性:
const queryClient = new QueryClient({ defaultOptions: { queries: { notifyOnChangeProps: 'tracked', }, }, }) function App() { return ( <QueryClientProvider client={queryClient}> <Example /> </QueryClientProvider> ) }
利用這個特性,你再也不用考慮重新渲染。當然這個特性也有一些限制,這就是為什么這個特性是一個可選項:
如果你使用對象剩余屬性結構的語法的話,最終所有屬性都會被追蹤。正常的解構語法是沒問題的,不要這么做:
// ???? will track all fields const { isLoading, ...queryInfo } = useQuery(...) // ? this is totally fine const { isLoading, data } = useQuery(...)
被追蹤的查詢只會追蹤render過程中用到的數據。如果你只在effects中用到了這些數據,他們并不會被追蹤。
const queryInfo = useQuery(...) // ???? will not corectly track data React.useEffect(() => { console.log(queryInfo.data) }) // ? fine because the dependency array is accessed during render React.useEffect(() => { console.log(queryInfo.data) }, [queryInfo.data])
被追蹤的查詢不會在每次render的時候被重置,所以只要你使用了一次某個數據,你就會在整個組件的生命周期內追蹤這個數據:
const queryInfo = useQuery(...) if (someCondition()) { // ???? we will track the data field if someCondition was true in any previous render cycle return <div>{queryInfo.data}</div> }
一個不同的但是并沒那么重要的React Query默認開啟的渲染優化是結構化共享。這個特性確保數據在所有地方是引用唯一的。舉個例子,假設我們有下面這個數據結構:
[ { "id": 1, "name": "Learn React", "status": "active" }, { "id": 2, "name": "Learn React Query", "status": "todo" } ]
現在假設我們將第一個todo轉為done,然后進行了一次后臺refetch。我們會從后端拿到一個全新的json:
{ "id": 1, "name": "Learn React", "status": "active" }, { "id": 1, "name": "Learn React", "status": "done" }, { "id": 2, "name": "Learn React Query", "status": "todo" } ]
現在React Query會嘗試對比新老狀態,盡可能多的復用老的狀態。在上面的例子中,todo數據會是一個新的對象,因為我們更新了一個todo。第一個id為1的對象也會是新的對象,但是對于id為2的對象我們會保持跟對應的舊數據一樣的引用-React Query會將他復制一份同樣的引用到新的數據,因為這部分數據并沒有發生變化。
這使得使用selector進行部分訂閱變得特別友好:
// ? will only re-render if something within todo with id:2 changes // thanks to structural sharing const { data } = useTodo(2)
就像我之前提到的,對于selector來說結構化共享會用到兩次:一次是在queryFn返回的結果上,另一次是在selector返回的結果上。在一些場景,特別是數據量比較大的場景,結構化共享會成為一個瓶頸。同時它只能使用在JSON可序列化的數據上。如果你不需要這個優化,你可以通過將`structuralSharing`設為false來關閉這個特性。
關于“ReactQuery渲染優化的方法是什么”的內容就介紹到這里了,感謝大家的閱讀。如果想了解更多行業相關的知識,可以關注億速云行業資訊頻道,小編每天都會為大家更新不同的知識點。
免責聲明:本站發布的內容(圖片、視頻和文字)以原創、轉載和分享為主,文章觀點不代表本網站立場,如果涉及侵權請聯系站長郵箱:is@yisu.com進行舉報,并提供相關證據,一經查實,將立刻刪除涉嫌侵權內容。