<kbd id="afajh"><form id="afajh"></form></kbd>
<strong id="afajh"><dl id="afajh"></dl></strong>
    <del id="afajh"><form id="afajh"></form></del>
        1. <th id="afajh"><progress id="afajh"></progress></th>
          <b id="afajh"><abbr id="afajh"></abbr></b>
          <th id="afajh"><progress id="afajh"></progress></th>

          大廠面經(jīng)---詳解react hooks面試題【React系列02】

          共 9788字,需瀏覽 20分鐘

           ·

          2021-03-31 11:32


          關(guān)注公眾號(hào) 前端人,回復(fù)“加群

          添加無(wú)廣告優(yōu)質(zhì)學(xué)習(xí)群

          1. 簡(jiǎn)單介紹下什么是hooks,hooks產(chǎn)生的背景?hooks的優(yōu)點(diǎn)?

          hooks是針對(duì)在使用react時(shí)存在以下問(wèn)題而產(chǎn)生的:

          • 組件之間復(fù)用狀態(tài)邏輯很難,在hooks之前,實(shí)現(xiàn)組件復(fù)用,一般采用高階組件和 Render Props,它們本質(zhì)是將復(fù)用邏輯提升到父組件中,很容易產(chǎn)生很多包裝組件,帶來(lái)嵌套地域。
          • 組件邏輯變得越來(lái)越復(fù)雜,尤其是生命周期函數(shù)中常常包含一些不相關(guān)的邏輯,完全不相關(guān)的代碼卻在同一個(gè)方法中組合在一起。如此很容易產(chǎn)生 bug,并且導(dǎo)致邏輯不一致。
          • 復(fù)雜的class組件,使用class組件,需要理解 JavaScript 中 this 的工作方式,不能忘記綁定事件處理器等操作,代碼復(fù)雜且冗余。除此之外,class組件也會(huì)讓一些react優(yōu)化措施失效。

          針對(duì)上面提到的問(wèn)題,react團(tuán)隊(duì)研發(fā)了hooks,它主要有兩方面作用:

          • 用于在函數(shù)組件中引入狀態(tài)管理和生命周期方法
          • 取代高階組件和render props來(lái)實(shí)現(xiàn)抽象和可重用性

          優(yōu)點(diǎn)也很明顯:

          • 避免在被廣泛使用的函數(shù)組件在后期迭代過(guò)程中,需要承擔(dān)一些副作用,而必須重構(gòu)成類組件,它幫助函數(shù)組件引入狀態(tài)管理和生命周期方法。

          • Hooks 出現(xiàn)之后,我們將復(fù)用邏輯提取到組件頂層,而不是強(qiáng)行提升到父組件中。這樣就能夠避免 HOC 和 Render Props 帶來(lái)的「嵌套地域」

          • 避免上面陳述的class組件帶來(lái)的那些問(wèn)題

          2. 知道hoc和render props嗎,它們有什么作用?有什么弊端?

          Render Props 組件和高階組件主要用來(lái)實(shí)現(xiàn)抽象和可重用性。

          弊端就是高階組件和 Render Props 本質(zhì)上都是將復(fù)用邏輯提升到父組件中,很容易產(chǎn)生很多包裝組件,帶來(lái)的「嵌套地域」。

          由于所有抽象邏輯都被其他 React 組件所隱藏,應(yīng)用變成了一棵沒有可讀性的組件樹。而那些可見的組件也很難在瀏覽器的 DOM 中進(jìn)行跟蹤。

          2.1 Render Props

          什么是Render Props

          render props模式是一種非常靈活復(fù)用性非常高的模式,它可以把特定行為或功能封裝成一個(gè)組件,提供給其他組件使用讓其他組件擁有這樣的能力。他把組件可以動(dòng)態(tài)渲染的地方暴露給外部,你不用再關(guān)注組件的內(nèi)部實(shí)現(xiàn),只要把數(shù)據(jù)通過(guò)函數(shù)傳出去就好。

          使用場(chǎng)景:

          • 通用業(yè)務(wù)邏輯的抽取
          • 當(dāng)兩個(gè)平級(jí)組件之間需要單向依賴的時(shí)候,比如兩個(gè)同級(jí)組件A、B,A組件需要跟隨B組件的內(nèi)部狀態(tài)來(lái)改變自己的內(nèi)部狀態(tài),我們就說(shuō)A依賴B;或者B依賴A

          2.2 Hoc

          hoc是 React 中用于重用組件邏輯的高級(jí)技術(shù),它是一個(gè)函數(shù),能夠接受一個(gè)組件并返回一個(gè)新的組件。
          實(shí)現(xiàn)高階組件的兩種方式:

          • 屬性代理。高階組件通過(guò)包裹的React組件來(lái)操作props
          • 反向繼承。高階組件繼承于被包裹的React組件

          2.2.1屬性代理

          a. 什么是屬性代理

          屬性代理組件繼承自React.Component,通過(guò)傳遞給被包裝的組件props得名

          // 屬性代理,把高階組件接收到的屬性傳遞給傳進(jìn)來(lái)的組件
          function HOC(WrappedComponent) {
            return class PP extends React.Component {
              render() {
                return <WrappedComponent {...this.props}/>
              }
            }

          b. 屬性代理的用途

          • 更改 props,可以對(duì)傳遞的包裹組件的WrappedComponent的props進(jìn)行控制
          • 通過(guò) refs 獲取組件實(shí)例
          /*
          可以通過(guò) ref 獲取關(guān)鍵詞 this(WrappedComponent 的實(shí)例)
          當(dāng) WrappedComponent 被渲染后,ref 上的回調(diào)函數(shù) proc 將會(huì)執(zhí)行,此時(shí)就有了這個(gè) WrappedComponent 的實(shí)例的引用
          */
          function refsHOC(WrappedComponent) {
            return class RefsHOC extends React.Component {
              proc(wrappedComponentInstance) {
                wrappedComponentInstance.method()
              }
              render() {
                const props = Object.assign({}, this.props, {ref: this.proc.bind(this)})
                return <WrappedComponent {...props}/>
              }
            }

          • 把 WrappedComponent 與其它 elements 包裝在一起

          2.1.2 反向繼承

          反向繼承是繼承自傳遞過(guò)來(lái)的組件

          function iiHOC(WrappedComponent) {
            return class Enhancer extends WrappedComponent {
              render() {
                return super.render()
              }
            }

          反向繼承允許高階組件通過(guò) this 關(guān)鍵詞獲取 WrappedComponent,意味著它可以獲取到 state,props,組件生命周期(component lifecycle)鉤子,以及渲染方法(render),所以我們主要用它來(lái)做渲染劫持,比如在渲染方法中讀取或更改 React Elements tree,或者有條件的渲染等。

          2.1.3 高階組件相關(guān)的面試題

          1. 這怎么在高階組件里面訪問(wèn)組件實(shí)例**
            答案見上面

          2. 你的項(xiàng)目中怎么使用的高階組件**
            a. 項(xiàng)目中經(jīng)常存在在配置系統(tǒng)中配置開關(guān)/全局常量,然后在頁(yè)面需要請(qǐng)求配置來(lái)做控制,如果在每個(gè)需要調(diào)用全局設(shè)置的地方都去請(qǐng)求一下接口,就會(huì)有一種不優(yōu)雅的感覺,這個(gè)時(shí)候我就想到利用高階組件抽象一下。
            b. 項(xiàng)目開發(fā)過(guò)程中,經(jīng)常也會(huì)遇到需要對(duì)當(dāng)前頁(yè)面的一些事件的默認(rèn)執(zhí)行做阻止,我們也可以寫一個(gè)高階組件等。

          3. hooks和hoc和render props有什么不同?

          它們之間最大的不同在于,后兩者僅僅是一種開發(fā)模式,而自定義的hooks是react提供的API模式,它既能更加自然的融入到react的渲染過(guò)程也更加符合react的函數(shù)編程理念。

          1. 介紹下常用的hooks?
          • useState(),狀態(tài)鉤子。為函數(shù)組建提供內(nèi)部狀態(tài)
          // 我們實(shí)現(xiàn)一個(gè)簡(jiǎn)易版的useState
          let memoizedStates = [ ]  // 多個(gè)useState 時(shí)需要使用數(shù)組來(lái)存
          let index = 0
          function useState (initialState) {
             memoizedStates[index] = memoizedStates[index] || initialState
             let currentIndex = index;
             function setState (newState) {
                memoizedStates[currentIndex] = newState
                render()
             }
             return [memoizedStates[index++], setState]

          • useContext(),共享鉤子。該鉤子的作用是,在組件之間共享狀態(tài)。可以解決react逐層通過(guò)Porps傳遞數(shù)據(jù),它接受React.createContext()的返回結(jié)果作為參數(shù),使用useContext將不再需要Provider 和 Consumer。

          • useReducer(),Action 鉤子。useReducer() 提供了狀態(tài)管理,其基本原理是通過(guò)用戶在頁(yè)面中發(fā)起action, 從而通過(guò)reducer方法來(lái)改變state, 從而實(shí)現(xiàn)頁(yè)面和狀態(tài)的通信。使用很像redux

          • useEffect(),副作用鉤子。它接收兩個(gè)參數(shù), 第一個(gè)是進(jìn)行的異步操作, 第二個(gè)是數(shù)組,用來(lái)給出Effect的依賴項(xiàng)

          • useRef(),獲取組件的實(shí)例;渲染周期之間共享數(shù)據(jù)的存儲(chǔ)(state不能存儲(chǔ)跨渲染周期的數(shù)據(jù),因?yàn)閟tate的保存會(huì)觸發(fā)組件重渲染)

          • useRef傳入一個(gè)參數(shù)initValue,并創(chuàng)建一個(gè)對(duì)象{ current: initValue }給函數(shù)組件使用,在整個(gè)生命周期中該對(duì)象保持不變。

          • useMemo和useCallback:可緩存函數(shù)的引用或值,useMemo用在計(jì)算值的緩存,注意不用濫用。經(jīng)常用在下面兩種場(chǎng)景(要保持引用相等;對(duì)于組件內(nèi)部用到的 object、array、函數(shù)等,如果用在了其他 Hook 的依賴數(shù)組中,或者作為 props 傳遞給了下游組件,應(yīng)該使用 useMemo/useCallback)

          • useLayoutEffect:會(huì)在所有的 DOM 變更之后同步調(diào)用 effect,可以使用它來(lái)讀取 DOM 布局并同步觸發(fā)重渲染

          • 描述下hooks下怎么模擬生命周期函數(shù),模擬的生命周期和class中的生命周期有什么區(qū)別嗎?

          // componentDidMount,必須加[],不然會(huì)默認(rèn)每次渲染都執(zhí)行
          useEffect(()=>{
          }, [])

          // componentDidUpdate
          useEffect(()=>{
          document.title = `You clicked ${count} times`;
          return()=>{
          // 以及 componentWillUnmount 執(zhí)行的內(nèi)容 
          }
          }, [count])

          //  shouldComponentUpdate, 只有 Parent 組件中的 count state 更新了,Child 才會(huì)重新渲染,否則不會(huì)。
          function Parent({
             const [count,setCount] = useState(0);
             const child = useMemo(()=> <Child count={count} />, [count]);
             return <>{count}</>
          }

          function Child(props{
              return <div>Count:{props.count}</div>

          這里有一個(gè)點(diǎn)需要注意,就是默認(rèn)的useEffect(不帶[])中return的清理函數(shù),它和componentWillUnmount有本質(zhì)區(qū)別的,默認(rèn)情況下return,在每次useEffect執(zhí)行前都會(huì)執(zhí)行,并不是只有組件卸載的時(shí)候執(zhí)行。
          useEffect在副作用結(jié)束之后,會(huì)延遲一段時(shí)間執(zhí)行,并非同步執(zhí)行,和compontDidMount有本質(zhì)區(qū)別。遇到dom操作,最好使用useLayoutEffect。

          1. hooks中的坑,以及為什么?
          • 不要在循環(huán),條件或嵌套函數(shù)中調(diào)用Hook,必須始終在React函數(shù)的頂層使用Hook。這是因?yàn)镽eact需要利用調(diào)用順序來(lái)正確更新相應(yīng)的狀態(tài),以及調(diào)用相應(yīng)的鉤子函數(shù)。一旦在循環(huán)或條件分支語(yǔ)句中調(diào)用Hook,就容易導(dǎo)致調(diào)用順序的不一致性,從而產(chǎn)生難以預(yù)料到的后果。

          • 使用useState時(shí)候,使用push,pop,splice等直接更改數(shù)組對(duì)象的坑,demo中使用push直接更改數(shù)組無(wú)法獲取到新值,應(yīng)該采用析構(gòu)方式,但是在class里面不會(huì)有這個(gè)問(wèn)題。(這個(gè)的原因是push,pop,splice是直接修改原數(shù)組,react會(huì)認(rèn)為state并沒有發(fā)生變化,無(wú)法更新)  這里的坑很多的,經(jīng)常出現(xiàn)的就是每次修改數(shù)組的時(shí)候:

          const [firstData, setFirstData]: any = useState([]);
          const handleFirstAdd = () => {
            // let temp = firstData
            // 不要這么寫,直接修改原數(shù)組相當(dāng)于沒有更新
            let temp = [...firstData];
            // 必須這么寫,多層數(shù)組也要這么寫
            temp.push({
              value"",
            });
            setFirstData(temp);
          };

          function Indicatorfilter({
            let [num, setNums] = useState([0123]);
            const test = () => {
              // 這里坑是直接采用push去更新num,setNums(num)是無(wú)法更新num的,必須使用num = [...num ,1]
              num.push(1);
              // num = [...num ,1]
              setNums(num);
            };
            return (
              <div className="filter">
                <div onClick={test}>測(cè)試</div>
                <div>
                  {num.map((item, index) => (
                    <div key={index}>{item}</div>
                  ))}
                </div>
              </div>

            );
          }

          class Indicatorfilter extends React.Component<anyany{
            constructor(props: any) {
              super(props);
              this.state = {
                nums: [123],
              };
              this.test = this.test.bind(this);
            }
            test() {
              // class采用同樣的方式是沒有問(wèn)題的
              this.state.nums.push(1);
              this.setState({
                numsthis.state.nums,
              });
            }
            render() {
              let { nums } = this.state;
              return (
                <div>
                  <div onClick={this.test}>測(cè)試</div>
                  <div>
                    {nums.map((item: any, index: number) => (
                      <div key={index}>{item}</div>
                    ))}
                  </div>
                </div>

              );
            }

          useState設(shè)置狀態(tài)的時(shí)候,只有第一次生效,后期需要更新狀態(tài),必須通過(guò)useEffect

          TableDeail是一個(gè)公共組件,在調(diào)用它的父組件里面,我們通過(guò)set改變columns的值,以為傳遞給TableDeail的columns是最新的值,所以tabColumn每次也是最新的值,但是實(shí)際tabColumn是最開始的值,不會(huì)隨著columns的更新而更新

          const TableDeail = ({
              columns,
          }:TableData) => {
              const [tabColumn, setTabColumn] = useState(columns) 
          }

          正確的做法是通過(guò)useEffect改變這個(gè)值

          const TableDeail = ({
              columns,
          }:TableData) => {
              const [tabColumn, setTabColumn] = useState(columns) 
              useEffect(() =>{setTabColumn(columns)},[columns])

          原文地址:https://blog.csdn.net/kellywong/article/details/106430977

          最后

          關(guān)注公眾號(hào),置頂公眾號(hào),鬼哥,一起前端進(jìn)階

          1. 公眾號(hào)里回復(fù)關(guān)鍵詞資料包領(lǐng)取我整理的進(jìn)階資料包
          2. 公眾號(hào)里回復(fù)關(guān)鍵詞加群,加入前端進(jìn)階群
          3. 文章點(diǎn)個(gè)在看,支持一下把!

          點(diǎn)擊關(guān)注我們↓


          題庫(kù)小程序


          瀏覽 73
          點(diǎn)贊
          評(píng)論
          收藏
          分享

          手機(jī)掃一掃分享

          分享
          舉報(bào)
          評(píng)論
          圖片
          表情
          推薦
          點(diǎn)贊
          評(píng)論
          收藏
          分享

          手機(jī)掃一掃分享

          分享
          舉報(bào)
          <kbd id="afajh"><form id="afajh"></form></kbd>
          <strong id="afajh"><dl id="afajh"></dl></strong>
            <del id="afajh"><form id="afajh"></form></del>
                1. <th id="afajh"><progress id="afajh"></progress></th>
                  <b id="afajh"><abbr id="afajh"></abbr></b>
                  <th id="afajh"><progress id="afajh"></progress></th>
                  青娱乐中文 | 五月成人色情网 | 最新在线看黄 | 婷婷视频在线观看 | www老鸭窝 |