<em>Mac</em>Book项目 2009年学校开始实施<em>Mac</em>Book项目,所有师生配备一本<em>Mac</em>Book,并同步更新了校园无线网络。学校每周进行电脑技术更新,每月发送技术支持资料,极大改变了教学及学习方式。因此2011
2021-06-01 09:32:01
Hooks 是react16.8新增特性,它可以使用一些state的新特性,簡化邏輯複用,副作用統一資料。
Hooks就是把某個目標結果鉤到某個可能會變化的資料來源或者事件源上,那麼當被鉤到的資料或者事件發生變化時,產生這個目標結果的程式碼會重新執行,產生更新後的結果。
Hook出世之前React存在的問題
在元件之間複用狀態邏輯很難
React 沒有提供將可複用性行為“附加”到元件的途徑(例如,把元件連線到 store)。有一些解決此類問題的方案,比如 render props 和 高階元件。但是這類方案需要重新組織你的元件結構,這可能會很麻煩,使你的程式碼難以理解。
複雜元件變得難以理解
元件常常在 componentDidMount
和 componentDidUpdate
中獲取資料。但是,同一個 componentDidMount
中可能也包含很多其它的邏輯,如設定事件監聽,而之後需在 componentWillUnmount
中清除。相互關聯且需要對照修改的程式碼被進行了拆分,而完全不相關的程式碼卻在同一個方法中組合在一起。如此很容易產生 bug,並且導致邏輯不一致。
難以理解的 class
class 是學習 React 的一大屏障。你必須去理解 JavaScript 中 this
的工作方式,這與其他語言存在巨大差異。還不能忘記繫結事件處理器。沒有穩定的語法提案,這些程式碼非常冗餘。大家可以很好地理解 props,state 和自頂向下的資料流,但對 class 卻一籌莫展。
Hook帶來的解決方案
useState
是react自帶的一個hook函數,它的作用就是用來宣告狀態變數。useState
這個函數接收的引數是我們的狀態初始值(initial state),它返回了一個陣列,這個陣列的第[0]
項是當前當前的狀態值,第[1]
項是可以改變狀態值的方法函數。
初始化
//返回一個 state,以及更新 state 的函數 setState(接收一個新的 state 值並將元件的一次重新渲染加入佇列) const [state, setState] = useState(initialState);
函數式更新
//如果新的 state 需要通過使用先前的 state 計算得出,那麼可以將函數傳遞給 setState。該函數將接收先前的 state,並返回一個更新後的值。 function Counter({initialCount}) { const [count, setCount] = useState(initialCount); return ( <> Count: {count} <button onClick={() => setCount(initialCount)}>Reset</button> <button onClick={() => setCount(prevCount => prevCount - 1)}>-</button> <button onClick={() => setCount(prevCount => prevCount + 1)}>+</button> </> ); }
惰性初始 state
//如果初始 state 需要通過複雜計算獲得,則可以傳入一個函數,在函數中計算並返回初始的 state,此函數只在初始渲染時被呼叫 const [state, setState] = useState(() => { const initialState = someExpensiveComputation(props); return initialState; });
跳過 state 更新
呼叫 State Hook 的更新函數並傳入當前的 state 時,React 將跳過子元件的渲染及 effect 的執行。(React 使用 Object.is
比較演演算法 來比較 state。)
useEffect
我們寫的有狀態元件,通常會產生很多的副作用(side effect),比如發起ajax請求獲取資料,新增一些監聽的註冊和取消註冊,手動修改dom等等。我們之前都把這些副作用的函數寫在生命週期函數勾點裡,比如componentDidMount
,componentDidUpdate
和componentWillUnmount
。而現在的useEffect就相當與這些宣告周期函數勾點的集合體。它以一抵三。
簡單例子
import { useState, useEffect } from 'react'; function Example() { const [count, setCount] = useState(0); // 類似於componentDidMount 和 componentDidUpdate: useEffect(() => { // 更新檔案的標題 document.title = `You clicked ${count} times`; }); return ( <div> <p>You clicked {count} times</p> <button onClick={() => setCount(count + 1)}> Click me </button> </div> ); }
清除 effect
通常,元件解除安裝時需要清除 effect 建立的諸如訂閱或計時器 ID 等資源。要實現這一點,useEffect
函數需返回一個清除函數。以下就是一個建立訂閱的例子:
useEffect(() => { const subscription = props.source.subscribe(); return () => { // 清除訂閱 subscription.unsubscribe(); }; });
為防止記憶體漏失,清除函數會在元件解除安裝前執行。另外,如果元件多次渲染(通常如此),則在執行下一個 effect 之前,上一個 effect 就已被清除。
effect 的執行時機
與 componentDidMount
、componentDidUpdate
不同的是,在瀏覽器完成佈局與繪製之後,傳給 useEffect
的函數會延遲呼叫。這使得它適用於許多常見的副作用場景,比如設定訂閱和事件處理等情況,因此不應在函數中執行阻塞瀏覽器更新螢幕的操作。
effect 的條件執行
預設情況下,effect 會在每輪元件渲染完成後執行。這樣的話,一旦 effect 的依賴發生變化,它就會被重新建立。在某些情況下,我們不需要在每次元件更新時都建立新的訂閱,而是僅需要在 source
prop 改變時重新建立。要實現這一點,可以給 useEffect
傳遞第二個引數,它是 effect 所依賴的值陣列。
//此時,只有當 props.source 改變後才會重新建立訂閱。(要實現componentDidMount功能只需要設定第二個引數為[]即可) useEffect( () => { const subscription = props.source.subscribe(); return () => { subscription.unsubscribe(); }; }, [props.source], );
useContext
可以深層元件傳值,父元件傳給子孫元件。接收一個 context 物件(React.createContext
的返回值)並返回該 context 的當前值。當前的 context 值由上層元件中距離當前元件最近的 <MyContext.Provider>
的 value
prop 決定。
當元件上層最近的 <MyContext.Provider>
更新時,該 Hook 會觸發重渲染,並使用最新傳遞給 MyContext
provider 的 context value
值。即使祖先使用 React.memo
或 shouldComponentUpdate
,也會在元件本身使用 useContext
時重新渲染
const themes = { light: { foreground: "#000000", background: "#eeeeee" }, dark: { foreground: "#ffffff", background: "#222222" } }; const ThemeContext = React.createContext(themes.light); function App() { return ( <ThemeContext.Provider value={themes.dark}> <Toolbar /> </ThemeContext.Provider> ); } function Toolbar(props) { return ( <div> <ThemedButton /> </div> ); } function ThemedButton() { const theme = useContext(ThemeContext); return ( <button style={{ background: theme.background, color: theme.foreground }}> I am styled by theme context! </button> ); }
useReducer
useState
的替代方案,可以用於複雜狀態處理。它接收一個形如 (state, action) => newState
的 reducer,並返回當前的 state 以及與其配套的 dispatch
方法。(如果你熟悉 Redux 的話,就已經知道它如何工作了。)參考 前端react面試題詳細解答
有兩種不同初始化 useReducer
state 的方式,你可以根據使用場景選擇其中的一種。將初始 state 作為第二個引數傳入 useReducer
是最簡單的方法:
//nst [state, dispatch] = useReducer(reducer, initialArg, init); const [state, dispatch] = useReducer( reducer, {count: initialCount} );
某些場景下,useReducer
會比 useState
更適用,例如 state 邏輯較複雜且包含多個子值,或者下一個 state 依賴於之前的 state 等。並且,使用 useReducer
還能給那些會觸發深更新的元件做效能優化,因為你可以向子元件傳遞 dispatch
而不是回撥函數 。
const initialState = {count: 0}; function reducer(state, action) { switch (action.type) { case 'increment': return {count: state.count + 1}; case 'decrement': return {count: state.count - 1}; default: throw new Error(); } } function Counter() { const [state, dispatch] = useReducer(reducer, initialState); return ( <> Count: {state.count} <button onClick={() => dispatch({type: 'decrement'})}>-</button> <button onClick={() => dispatch({type: 'increment'})}>+</button> </> ); }
你可以選擇惰性地建立初始 state。為此,需要將 init
函數作為 useReducer
的第三個引數傳入,這樣初始 state 將被設定為 init(initialArg)
。
這麼做可以將用於計算 state 的邏輯提取到 reducer 外部,這也為將來對重置 state 的 action 做處理提供了便利:
function init(initialCount) { return {count: initialCount}; } function reducer(state, action) { switch (action.type) { case 'increment': return {count: state.count + 1}; case 'decrement': return {count: state.count - 1}; case 'reset': return init(action.payload); default: throw new Error(); } } function Counter({initialCount}) { const [state, dispatch] = useReducer(reducer, initialCount, init); return ( <> Count: {state.count} <button onClick={() => dispatch({type: 'reset', payload: initialCount})}> Reset </button> <button onClick={() => dispatch({type: 'decrement'})}>-</button> <button onClick={() => dispatch({type: 'increment'})}>+</button> </> ); }
跳過 dispatch
如果 Reducer Hook 的返回值與當前 state 相同,React 將跳過子元件的渲染及副作用的執行。(React 使用 Object.is
比較演演算法 來比較 state。)
useMemo
把“建立”函數和依賴項陣列作為引數傳入 useMemo
,它僅會在某個依賴項改變時才重新計算 memoized 值。這種優化有助於避免在每次渲染時都進行高開銷的計算。如果沒有提供依賴項陣列,useMemo
在每次渲染時都會計算新的值。memo
是淺比較,意思是,物件只比較記憶體地址,只要你記憶體地址沒變,管你物件裡面的值千變萬化都不會觸發render。
你可以把 useMemo
作為效能優化的手段,但不要把它當成語意上的保證。將來,React 可能會選擇“遺忘”以前的一些 memoized 值,並在下次渲染時重新計算它們,比如為離屏元件釋放記憶體。先編寫在沒有 useMemo
的情況下也可以執行的程式碼 —— 之後再在你的程式碼中新增 useMemo
,以達到優化效能的目的。
const memoizedValue = useMemo(() => computeExpensiveValue(a, b), [a, b]);
useCallback
把內聯回撥函數及依賴項陣列作為引數傳入 useCallback
,它將返回該回撥函數的 memoized 版本,該回撥函數僅在某個依賴項改變時才會更新。當你把回撥函數傳遞給經過優化的並使用參照相等性去避免非必要渲染(例如 shouldComponentUpdate
)的子元件時,它將非常有用。
useMemo
與 useCallback
類似,都是有著快取的作用,useMemo 是快取值的,useCallback 是快取函數的。
useCallback(fn, deps)
相當於 useMemo(() => fn, deps)
。
const memoizedCallback = useCallback( () => { doSomething(a, b); }, [a, b], );
useRef
useRef
返回一個可變的 ref 物件,其 .current
屬性被初始化為傳入的引數(initialValue
)。返回的 ref 物件在元件的整個生命週期內保持不變。
useEffect
裡面的state的值,是固定的,這個是有辦法解決的,就是用useRef
,可以理解成useRef
的一個作用:就是相當於全域性作用域,一處被修改,其他地方全更新。
本質上,useRef
就像是可以在其 .current
屬性中儲存一個可變值的“盒子”。你應該熟悉 ref 這一種存取 DOM 的主要方式。如果你將 ref 物件以 <div ref={myRef} />
形式傳入元件,則無論該節點如何改變,React 都會將 ref 物件的 .current
屬性設定為相應的 DOM 節點。然而,useRef()
比 ref
屬性更有用。它可以很方便地儲存任何可變值,其類似於在 class 中使用範例欄位的方式。
請記住,當 ref 物件內容發生變化時,useRef
並不會通知你。變更 .current
屬性不會引發元件重新渲染。如果想要在 React 繫結或解綁 DOM 節點的 ref 時執行某些程式碼,則需要使用回撥 ref 來實現。
const Hook =()=>{ const [count, setCount] = useState(0) const btnRef = useRef(null) useEffect(() => { console.log('use effect...') const onClick = ()=>{ setCount(count+1) } btnRef.current.addEventListener('click',onClick, false) return ()=> btnRef.current.removeEventListener('click',onClick, false) },[count]) return( <div> <div> {count} </div> <button ref={btnRef}>click me </button> </div> ) }
useImperativeHandle
useImperativeHandle(ref, createHandle, [deps])
useImperativeHandle
可以讓你在使用 ref
時自定義暴露給父元件的範例值。在大多數情況下,應當避免使用 ref 這樣的命令式程式碼。useImperativeHandle
應當與 forwardRef
一起使用:
function FancyInput(props, ref) { const inputRef = useRef(); useImperativeHandle(ref, () => ({ focus: () => { inputRef.current.focus(); } })); return <input ref={inputRef} ... />; } FancyInput = forwardRef(FancyInput);
在本例中,渲染 <FancyInput ref={inputRef} />
的父元件可以呼叫 inputRef.current.focus()
。
自定義 Hook 是一個函數,其名稱以 “use
” 開頭,函數內部可以呼叫其他的 Hook。
例如,下面的 useFriendStatus
是我們第一個自定義的 Hook:
import { useState, useEffect } from 'react'; function useFriendStatus(friendID) { const [isOnline, setIsOnline] = useState(null); useEffect(() => { function handleStatusChange(status) { setIsOnline(status.isOnline); } ChatAPI.subscribeToFriendStatus(friendID, handleStatusChange); return () => { ChatAPI.unsubscribeFromFriendStatus(friendID, handleStatusChange); }; }); return isOnline; }
自定義一個當resize 的時候 監聽window的width和height的hook
import {useEffect, useState} from "react"; export const useWindowSize = () => { const [width, setWidth] = useState() const [height, setHeight] = useState() useEffect(() => { const {clientWidth, clientHeight} = document.documentElement setWidth(clientWidth) setHeight(clientHeight) }, []) useEffect(() => { const handleWindowSize = () =>{ const {clientWidth, clientHeight} = document.documentElement setWidth(clientWidth) setHeight(clientHeight) }; window.addEventListener('resize', handleWindowSize, false) return () => { window.removeEventListener('resize',handleWindowSize, false) } }) return [width, height] }
使用:
const [width, height] = useWindowSize() const isOnline = useFriendStatus(id);
到此這篇關於React-hooks面試考察知識點彙總的文章就介紹到這了,更多相關React-hooks面試內容請搜尋it145.com以前的文章或繼續瀏覽下面的相關文章希望大家以後多多支援it145.com!
相關文章
<em>Mac</em>Book项目 2009年学校开始实施<em>Mac</em>Book项目,所有师生配备一本<em>Mac</em>Book,并同步更新了校园无线网络。学校每周进行电脑技术更新,每月发送技术支持资料,极大改变了教学及学习方式。因此2011
2021-06-01 09:32:01
综合看Anker超能充系列的性价比很高,并且与不仅和iPhone12/苹果<em>Mac</em>Book很配,而且适合多设备充电需求的日常使用或差旅场景,不管是安卓还是Switch同样也能用得上它,希望这次分享能给准备购入充电器的小伙伴们有所
2021-06-01 09:31:42
除了L4WUDU与吴亦凡已经多次共事,成为了明面上的厂牌成员,吴亦凡还曾带领20XXCLUB全队参加2020年的一场音乐节,这也是20XXCLUB首次全员合照,王嗣尧Turbo、陈彦希Regi、<em>Mac</em> Ova Seas、林渝植等人全部出场。然而让
2021-06-01 09:31:34
目前应用IPFS的机构:1 谷歌<em>浏览器</em>支持IPFS分布式协议 2 万维网 (历史档案博物馆)数据库 3 火狐<em>浏览器</em>支持 IPFS分布式协议 4 EOS 等数字货币数据存储 5 美国国会图书馆,历史资料永久保存在 IPFS 6 加
2021-06-01 09:31:24
开拓者的车机是兼容苹果和<em>安卓</em>,虽然我不怎么用,但确实兼顾了我家人的很多需求:副驾的门板还配有解锁开关,有的时候老婆开车,下车的时候偶尔会忘记解锁,我在副驾驶可以自己开门:第二排设计很好,不仅配置了一个很大的
2021-06-01 09:30:48
不仅是<em>安卓</em>手机,苹果手机的降价力度也是前所未有了,iPhone12也“跳水价”了,发布价是6799元,如今已经跌至5308元,降价幅度超过1400元,最新定价确认了。iPhone12是苹果首款5G手机,同时也是全球首款5nm芯片的智能机,它
2021-06-01 09:30:45