溫馨提示×

溫馨提示×

您好,登錄后才能下訂單哦!

密碼登錄×
登錄注冊×
其他方式登錄
點擊 登錄注冊 即表示同意《億速云用戶服務(wù)條款》

React hooks面試考察的知識點有哪些

發(fā)布時間:2022-10-10 14:27:24 來源:億速云 閱讀:143 作者:iii 欄目:開發(fā)技術(shù)

這篇文章主要介紹了React hooks面試考察的知識點有哪些的相關(guān)知識,內(nèi)容詳細(xì)易懂,操作簡單快捷,具有一定借鑒價值,相信大家閱讀完這篇React hooks面試考察的知識點有哪些文章都會有所收獲,下面我們一起來看看吧。

    什么是hooks?解決了什么問題?

    Hooks 是react16.8新增特性,它可以使用一些state的新特性,簡化邏輯復(fù)用,副作用統(tǒng)一數(shù)據(jù)。

    Hooks就是把某個目標(biāo)結(jié)果鉤到某個可能會變化的數(shù)據(jù)源或者事件源上,那么當(dāng)被鉤到的數(shù)據(jù)或者事件發(fā)生變化時,產(chǎn)生這個目標(biāo)結(jié)果的代碼會重新執(zhí)行,產(chǎn)生更新后的結(jié)果。

    Hook 簡介

    Hook出世之前React存在的問題

    • 在組件之間復(fù)用狀態(tài)邏輯很難

      React 沒有提供將可復(fù)用性行為“附加”到組件的途徑(例如,把組件連接到 store)。有一些解決此類問題的方案,比如 render props 和 高階組件。但是這類方案需要重新組織你的組件結(jié)構(gòu),這可能會很麻煩,使你的代碼難以理解。

    • 復(fù)雜組件變得難以理解

      組件常常在 componentDidMount 和 componentDidUpdate中獲取數(shù)據(jù)。但是,同一個 componentDidMount 中可能也包含很多其它的邏輯,如設(shè)置事件監(jiān)聽,而之后需在 componentWillUnmount 中清除。相互關(guān)聯(lián)且需要對照修改的代碼被進行了拆分,而完全不相關(guān)的代碼卻在同一個方法中組合在一起。如此很容易產(chǎn)生 bug,并且導(dǎo)致邏輯不一致。

    • 難以理解的 class

      class 是學(xué)習(xí) React 的一大屏障。你必須去理解 JavaScript 中 this 的工作方式,這與其他語言存在巨大差異。還不能忘記綁定事件處理器。沒有穩(wěn)定的語法提案,這些代碼非常冗余。大家可以很好地理解 props,state 和自頂向下的數(shù)據(jù)流,但對 class 卻一籌莫展。

    Hook帶來的解決方案

    • 你可以使用 Hook 從組件中提取狀態(tài)邏輯,使得這些邏輯可以單獨測試并復(fù)用。Hook 使你在無需修改組件結(jié)構(gòu)的情況下復(fù)用狀態(tài)邏輯。

    • Hook 將組件中相互關(guān)聯(lián)的部分拆分成更小的函數(shù)(比如設(shè)置訂閱或請求數(shù)據(jù)),而并非強制按照生命周期劃分。你還可以使用 reducer 來管理組件的內(nèi)部狀態(tài),使其更加可預(yù)測。

    • Hook 使你在非 class 的情況下可以使用更多的 React 特性。 從概念上講,React 組件一直更像是函數(shù)。而 Hook 則擁抱了函數(shù),同時也沒有犧牲 React 的精神原則。Hook 提供了問題的解決方案,無需學(xué)習(xí)復(fù)雜的函數(shù)式或響應(yīng)式編程技術(shù)。

    Hook API

    useState

    useState 是react自帶的一個hook函數(shù),它的作用就是用來聲明狀態(tài)變量。useState這個函數(shù)接收的參數(shù)是我們的狀態(tài)初始值(initial state),它返回了一個數(shù)組,這個數(shù)組的第[0]項是當(dāng)前當(dāng)前的狀態(tài)值,第[1]項是可以改變狀態(tài)值的方法函數(shù)。

    初始化

    //返回一個 state,以及更新 state 的函數(shù) setState(接收一個新的 state 值并將組件的一次重新渲染加入隊列)
    const [state, setState] = useState(initialState);

    函數(shù)式更新

    //如果新的 state 需要通過使用先前的 state 計算得出,那么可以將函數(shù)傳遞給 setState。該函數(shù)將接收先前的 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 需要通過復(fù)雜計算獲得,則可以傳入一個函數(shù),在函數(shù)中計算并返回初始的 state,此函數(shù)只在初始渲染時被調(diào)用
    const [state, setState] = useState(() => {
      const initialState = someExpensiveComputation(props);
      return initialState;
    });

    跳過 state 更新

    調(diào)用 State Hook 的更新函數(shù)并傳入當(dāng)前的 state 時,React 將跳過子組件的渲染及 effect 的執(zhí)行。(React 使用 Object.is 比較算法 來比較 state。)

    useEffect

    我們寫的有狀態(tài)組件,通常會產(chǎn)生很多的副作用(side effect),比如發(fā)起ajax請求獲取數(shù)據(jù),添加一些監(jiān)聽的注冊和取消注冊,手動修改dom等等。我們之前都把這些副作用的函數(shù)寫在生命周期函數(shù)鉤子里,比如componentDidMount,componentDidUpdatecomponentWillUnmount。而現(xiàn)在的useEffect就相當(dāng)與這些聲明周期函數(shù)鉤子的集合體。它以一抵三。

    簡單例子

    import { useState, useEffect } from 'react';
    
    function Example() {
      const [count, setCount] = useState(0);
    
      // 類似于componentDidMount 和 componentDidUpdate:
      useEffect(() => {
        // 更新文檔的標(biāo)題
        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 創(chuàng)建的諸如訂閱或計時器 ID 等資源。要實現(xiàn)這一點,useEffect 函數(shù)需返回一個清除函數(shù)。以下就是一個創(chuàng)建訂閱的例子:

    useEffect(() => {
      const subscription = props.source.subscribe();
      return () => {
        // 清除訂閱
        subscription.unsubscribe();
      };
    });

    為防止內(nèi)存泄漏,清除函數(shù)會在組件卸載前執(zhí)行。另外,如果組件多次渲染(通常如此),則在執(zhí)行下一個 effect 之前,上一個 effect 就已被清除。

    effect 的執(zhí)行時機

    componentDidMount、componentDidUpdate 不同的是,在瀏覽器完成布局與繪制之后,傳給 useEffect 的函數(shù)會延遲調(diào)用。這使得它適用于許多常見的副作用場景,比如設(shè)置訂閱和事件處理等情況,因此不應(yīng)在函數(shù)中執(zhí)行阻塞瀏覽器更新屏幕的操作。

    effect 的條件執(zhí)行

    默認(rèn)情況下,effect 會在每輪組件渲染完成后執(zhí)行。這樣的話,一旦 effect 的依賴發(fā)生變化,它就會被重新創(chuàng)建。在某些情況下,我們不需要在每次組件更新時都創(chuàng)建新的訂閱,而是僅需要在 source prop 改變時重新創(chuàng)建。要實現(xiàn)這一點,可以給 useEffect 傳遞第二個參數(shù),它是 effect 所依賴的值數(shù)組。

    //此時,只有當(dāng) props.source 改變后才會重新創(chuàng)建訂閱。(要實現(xiàn)componentDidMount功能只需要設(shè)置第二個參數(shù)為[]即可)
    useEffect(
      () => {
        const subscription = props.source.subscribe();
        return () => {
          subscription.unsubscribe();
        };
      },
      [props.source],
    );

    useContext

    可以深層組件傳值,父組件傳給子孫組件。接收一個 context 對象(React.createContext 的返回值)并返回該 context 的當(dāng)前值。當(dāng)前的 context 值由上層組件中距離當(dāng)前組件最近的 <MyContext.Provider>value prop 決定。

    當(dāng)組件上層最近的 <MyContext.Provider> 更新時,該 Hook 會觸發(fā)重渲染,并使用最新傳遞給 MyContext provider 的 context value 值。即使祖先使用 React.memoshouldComponentUpdate,也會在組件本身使用 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 的替代方案,可以用于復(fù)雜狀態(tài)處理。它接收一個形如 (state, action) => newState 的 reducer,并返回當(dāng)前的 state 以及與其配套的 dispatch 方法。(如果你熟悉 Redux 的話,就已經(jīng)知道它如何工作了。)參考 前端react面試題詳細(xì)解答

    指定初始 state

    有兩種不同初始化 useReducer state 的方式,你可以根據(jù)使用場景選擇其中的一種。將初始 state 作為第二個參數(shù)傳入 useReducer 是最簡單的方法:

    //nst [state, dispatch] = useReducer(reducer, initialArg, init);
     const [state, dispatch] = useReducer(
        reducer,
        {count: initialCount}
     );

    某些場景下,useReducer 會比 useState 更適用,例如 state 邏輯較復(fù)雜且包含多個子值,或者下一個 state 依賴于之前的 state 等。并且,使用 useReducer 還能給那些會觸發(fā)深更新的組件做性能優(yōu)化,因為你可以向子組件傳遞 dispatch 而不是回調(diào)函數(shù) 。

    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>
        </>
      );
    }

    惰性初始化

    你可以選擇惰性地創(chuàng)建初始 state。為此,需要將 init 函數(shù)作為 useReducer 的第三個參數(shù)傳入,這樣初始 state 將被設(shè)置為 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 的返回值與當(dāng)前 state 相同,React 將跳過子組件的渲染及副作用的執(zhí)行。(React 使用 Object.is 比較算法 來比較 state。)

    useMemo

    把“創(chuàng)建”函數(shù)和依賴項數(shù)組作為參數(shù)傳入 useMemo,它僅會在某個依賴項改變時才重新計算 memoized 值。這種優(yōu)化有助于避免在每次渲染時都進行高開銷的計算。如果沒有提供依賴項數(shù)組,useMemo 在每次渲染時都會計算新的值。memo是淺比較,意思是,對象只比較內(nèi)存地址,只要你內(nèi)存地址沒變,管你對象里面的值千變?nèi)f化都不會觸發(fā)render。

    你可以把 useMemo 作為性能優(yōu)化的手段,但不要把它當(dāng)成語義上的保證。將來,React 可能會選擇“遺忘”以前的一些 memoized 值,并在下次渲染時重新計算它們,比如為離屏組件釋放內(nèi)存。先編寫在沒有 useMemo 的情況下也可以執(zhí)行的代碼 &mdash;&mdash; 之后再在你的代碼中添加 useMemo,以達(dá)到優(yōu)化性能的目的。

    const memoizedValue = useMemo(() => computeExpensiveValue(a, b), [a, b]);

    useCallback

    把內(nèi)聯(lián)回調(diào)函數(shù)及依賴項數(shù)組作為參數(shù)傳入 useCallback,它將返回該回調(diào)函數(shù)的 memoized 版本,該回調(diào)函數(shù)僅在某個依賴項改變時才會更新。當(dāng)你把回調(diào)函數(shù)傳遞給經(jīng)過優(yōu)化的并使用引用相等性去避免非必要渲染(例如 shouldComponentUpdate)的子組件時,它將非常有用。

    useMemouseCallback 類似,都是有著緩存的作用,useMemo 是緩存值的,useCallback 是緩存函數(shù)的。

    useCallback(fn, deps) 相當(dāng)于 useMemo(() => fn, deps)。

    const memoizedCallback = useCallback(
      () => {
        doSomething(a, b);
      },
      [a, b],
    );

    useRef

    useRef 返回一個可變的 ref 對象,其 .current 屬性被初始化為傳入的參數(shù)(initialValue)。返回的 ref 對象在組件的整個生命周期內(nèi)保持不變。

    useEffect里面的state的值,是固定的,這個是有辦法解決的,就是用useRef,可以理解成useRef的一個作用:就是相當(dāng)于全局作用域,一處被修改,其他地方全更新。

    本質(zhì)上,useRef 就像是可以在其 .current 屬性中保存一個可變值的“盒子”。你應(yīng)該熟悉 ref 這一種訪問 DOM 的主要方式。如果你將 ref 對象以 <div ref={myRef} /> 形式傳入組件,則無論該節(jié)點如何改變,React 都會將 ref 對象的 .current 屬性設(shè)置為相應(yīng)的 DOM 節(jié)點。然而,useRef()ref 屬性更有用。它可以很方便地保存任何可變值,其類似于在 class 中使用實例字段的方式。

    請記住,當(dāng) ref 對象內(nèi)容發(fā)生變化時,useRef 并不會通知你。變更 .current 屬性不會引發(fā)組件重新渲染。如果想要在 React 綁定或解綁 DOM 節(jié)點的 ref 時運行某些代碼,則需要使用回調(diào) ref 來實現(xiàn)。

    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 時自定義暴露給父組件的實例值。在大多數(shù)情況下,應(yīng)當(dāng)避免使用 ref 這樣的命令式代碼。useImperativeHandle 應(yīng)當(dāng)與 forwardRef 一起使用:

    function FancyInput(props, ref) {
      const inputRef = useRef();
      useImperativeHandle(ref, () => ({
        focus: () => {
          inputRef.current.focus();
        }
      }));
      return <input ref={inputRef} ... />;
    }
    FancyInput = forwardRef(FancyInput);

    在本例中,渲染 <FancyInput ref={inputRef} /> 的父組件可以調(diào)用 inputRef.current.focus()。

    自定義 Hook

    自定義 Hook 是一個函數(shù),其名稱以 “use” 開頭,函數(shù)內(nèi)部可以調(diào)用其他的 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;
    }

    自定義一個當(dāng)resize 的時候 監(jiān)聽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);

    關(guān)于“React hooks面試考察的知識點有哪些”這篇文章的內(nèi)容就介紹到這里,感謝各位的閱讀!相信大家對“React hooks面試考察的知識點有哪些”知識都有一定的了解,大家如果還想學(xué)習(xí)更多知識,歡迎關(guān)注億速云行業(yè)資訊頻道。

    向AI問一下細(xì)節(jié)

    免責(zé)聲明:本站發(fā)布的內(nèi)容(圖片、視頻和文字)以原創(chuàng)、轉(zhuǎn)載和分享為主,文章觀點不代表本網(wǎng)站立場,如果涉及侵權(quán)請聯(lián)系站長郵箱:is@yisu.com進行舉報,并提供相關(guān)證據(jù),一經(jīng)查實,將立刻刪除涉嫌侵權(quán)內(nèi)容。

    AI