国产成人精品18p,天天干成人网,无码专区狠狠躁天天躁,美女脱精光隐私扒开免费观看

React Hooks使用常見(jiàn)的坑

發(fā)布時(shí)間:2021-08-17 12:16 來(lái)源: 閱讀:0 作者:孤舟蓑翁 欄目: JavaScript 歡迎投稿:712375056

React Hooks 是 React 16.8 引入的新特性,允許我們在不使用 Class 的前提下使用 state 和其他特性。React Hooks 要解決的問(wèn)題是狀態(tài)共享,是繼 render-props 和 higher-order components 之后的第三種狀態(tài)邏輯復用方案,不會(huì )產(chǎn)生 JSX 嵌套地獄問(wèn)題。

為什么會(huì )有Hooks?

介紹Hooks之前,首先要給大家說(shuō)一下React的組件創(chuàng )建方式,一種是類(lèi)組件,一種是純函數組件,并且React團隊希望,組件不要變成復雜的容器,最好只是數據流的管道。開(kāi)發(fā)者根據需要,組合管道即可。也就是說(shuō)組件的最佳寫(xiě)法應該是函數,而不是類(lèi)。。

函數組件比類(lèi)組件更加方便實(shí)現業(yè)務(wù)邏輯代碼的分離和組件的復用,函數組件也比類(lèi)組件輕量,沒(méi)有react hooks之前,函數組件是無(wú)法實(shí)現LocalState的,這導致有localstate狀態(tài)的組件無(wú)法用函數組件來(lái)書(shū)寫(xiě),這限制了函數組件的應用范圍,而react hooks擴展了函數組件的能力??墒窃谑褂玫倪^(guò)程中,也要注意下面這些問(wèn)題,否則就會(huì )掉進(jìn)坑里,造成性能損失。按照下面的方法做,,才能避開(kāi)這些陷阱。

1. 將與狀態(tài)改變無(wú)關(guān)的變量和方法提取到組件函數外面

每次狀態(tài)改變時(shí),整個(gè)函數組件都會(huì )重新執行一遍。導致函數組件內部定義的方法和變量,都會(huì )重新創(chuàng )建,重新給它們分配內存,這會(huì )導致性能受到影響。

import React, {useState,useCallback} from "react";

// 測試每次狀態(tài)改變時(shí),方法是不是重新分配內存
let testFooMemoAlloc = new Set();

const Page = (props:any) => {
  console.log('每次狀態(tài)改變,函數組件從頭開(kāi)始執行')
  const [count, setCount] = useState(0);
  const calc = () => {
    setCount(count + 1);
  }

  const bar = {
    a:1,
    b:2,
    c: '與狀態(tài)無(wú)關(guān)的變量定義'
  }
 
  const doFoo = () => {
    console.log('與狀態(tài)無(wú)關(guān)的方法');

  }
  testFooMemoAlloc.add(doFoo)
  
  return (
    <>
      <button onClick={calc}>加1</button>
      <p>count:{count}</p>
      <p>testFooMemoAlloc.size增加的話(huà),說(shuō)明每次都重新分配了內存:{testFooMemoAlloc.size}</p>
    </>
  )
}

export default Page;

與改變狀態(tài)相關(guān)的變量和方法,必須放在hooks組件內,而無(wú)狀態(tài)無(wú)關(guān)的變量和方法,可以提取到函數組件外,避免每次狀態(tài)更新,都重新分配內存。也可以分別使用useMemo和useCallback包裹變量與函數,也能達到同樣的效果,后面會(huì )講。

import React, {useState,useCallback} from "react";

// 測試每次狀態(tài)改變時(shí),方法是不是重新分配內存
let testFooMemoAlloc = new Set();

const bar = {
  a:1,
  b:2,
  c: '與狀態(tài)無(wú)關(guān)的變量定義'
}

const doFoo = () => {
  console.log('與狀態(tài)無(wú)關(guān)的方法');

}

const Page = (props:any) => {
  console.log('每次狀態(tài)改變,函數組件從頭開(kāi)始執行')
  const [count, setCount] = useState(0);
  const calc = () => {
    setCount(count + 1);
  }

  testFooMemoAlloc.add(doFoo)
  
  return (
    <>
      <button onClick={calc}>加1</button>
      <p>count:{count}</p>
      <p>testFooMemoAlloc.size增加的話(huà),說(shuō)明每次都重新分配了內存:{testFooMemoAlloc.size}</p>
    </>
  )
}

export default Page;

2. 用memo對子組件進(jìn)行包裝

父組件引入子組件,會(huì )造成一些不必要的重復渲染,每次父組件更新count,子組件都會(huì )更新。

import React,{useState} from "react";
const Child = (props:any) => {
    console.log('子組件?')
    return(
        <div>我是一個(gè)子組件</div>
    );
}
const Page = (props:any) => {
    const [count, setCount] = useState(0);
    return (
        <>
            <button onClick={(e) => { setCount(count+1) }}>加1</button>
            <p>count:{count}</p>
            <Child />
        </>
    )
}

export default Page;

使用memo,count變化子組件沒(méi)有更新

import React,{useState,memo} from "react";
const Child = memo((props:any) => {
    console.log('子組件?')
    return(
        <div>我是一個(gè)子組件</div>
    );
})
const Page = (props:any) => {
    const [count, setCount] = useState(0);
    return (
        <>
            <button onClick={(e) => { setCount(count+1) }}>加1</button>
            <p>count:{count}</p>
            <Child />
        </>
    )
}

export default Page;

給memo傳入第二個(gè)參數,開(kāi)啟對象深度比較。當子組件傳遞的屬性值未發(fā)生改變時(shí),子組件不會(huì )做無(wú)意義的render。

memo不僅適用于函數組件,也適用于class組件,是一個(gè)高階組件,默認情況下只會(huì )對復雜對象做淺層比較,如果想做深度比較,可以傳入第二個(gè)參數。與shouldComponentUpdate不同的是,deepCompare返回true時(shí),不會(huì )觸發(fā) render,如果返回false,則會(huì )。而shouldComponentUpdate剛好與其相反。

import React, {useState, memo } from "react";
import deepCompare from "./deepCompare";

const Child = memo((props:any) => {
    console.log('子組件')
  return (
      <>
      <div>我是一個(gè)子組件</div>
      <div>{ props.fooObj.a}</div>
      </>
    );
}, deepCompare)

const Page = (props:any) => {
  const [count, setCount] = useState(0);
  const [fooObj, setFooObj] = useState({ a: 1, b: { c: 2 } })
  console.log('頁(yè)面開(kāi)始渲染')
  const calc = () => {
    setCount(count + 1);
    if (count === 3) {
      setFooObj({ b: { c: 2 }, a: count })
    }
  }
  const doBar = () => {
    console.log('給子組件傳遞方法,測試一下是否會(huì )引起不必須的渲染')
  }
    return (
        <>
        <button onClick={calc}>加1</button>
        <p>count:{count}</p>
        <Child fooObj={fooObj} doBar={doBar} />
        </>
    )
}

export default Page;
// 深度比較兩個(gè)對象是否相等
export default function deepCompare(prevProps: any, nextProps: any) {
  const len: number = arguments.length;
  let leftChain: any[] = [];
  let rightChain: any = [];
  // // console.log({ arguments });
  //
  if (len < 2) {
    // console.log('需要傳入2個(gè)對象,才能進(jìn)行兩個(gè)對象的屬性對比');
    return true;
  }
  // for (let i = 1; i < len; i++) {
  // leftChain = [];
  // rightChain = [];
  console.log({ prevProps, nextProps });
  if (!compare2Objects(prevProps, nextProps, leftChain, rightChain)) {
    // console.log('兩個(gè)對象不相等');
    return false;
  }
  // }
  // console.log('兩個(gè)對象相等');

  return true;
}

function compare2Objects(prevProps: any, nextProps: any, leftChain: any, rightChain: any) {
  var p;

  // 兩個(gè)值都為為NaN時(shí),在js中是不相等的, 而在這里認為相等才是合理的
  if (isNaN(prevProps) && isNaN(nextProps) && typeof prevProps === 'number' && typeof nextProps === 'number') {
    return true;
  }

  // 原始值比較
  if (prevProps === nextProps) {
    console.log('原始值', prevProps, nextProps);
    return true;
  }

  // 構造類(lèi)型比較
  if (
    (typeof prevProps === 'function' && typeof nextProps === 'function') ||
    (prevProps instanceof Date && nextProps instanceof Date) ||
    (prevProps instanceof RegExp && nextProps instanceof RegExp) ||
    (prevProps instanceof String && nextProps instanceof String) ||
    (prevProps instanceof Number && nextProps instanceof Number)
  ) {
    console.log('function', prevProps.toString() === nextProps.toString());
    return prevProps.toString() === nextProps.toString();
  }

  // 兩個(gè)比較變量的值如果是null和undefined,在這里會(huì )退出
  if (!(prevProps instanceof Object && nextProps instanceof Object)) {
    console.log(prevProps, nextProps, 'prevProps instanceof Object && nextProps instanceof Object');
    return false;
  }

  if (prevProps.isPrototypeOf(nextProps) || nextProps.isPrototypeOf(prevProps)) {
    console.log('prevProps.isPrototypeOf(nextProps) || nextProps.isPrototypeOf(prevProps)');
    return false;
  }

  // 構造器不相等則兩個(gè)對象不相等
  if (prevProps.constructor !== nextProps.constructor) {
    console.log('prevProps.constructor !== nextProps.constructor');
    return false;
  }

  // 原型不相等則兩個(gè)對象不相等
  if (prevProps.prototype !== nextProps.prototype) {
    console.log('prevProps.prototype !== nextProps.prototype');
    return false;
  }

  if (leftChain.indexOf(prevProps) > -1 || rightChain.indexOf(nextProps) > -1) {
    console.log('leftChain.indexOf(prevProps) > -1 || rightChain.indexOf(nextProps) > -1');
    return false;
  }

  // 遍歷下次的屬性對象,優(yōu)先比較不相等的情形
  for (p in nextProps) {
    if (nextProps.hasOwnProperty(p) !== prevProps.hasOwnProperty(p)) {
      console.log('nextProps.hasOwnProperty(p) !== prevProps.hasOwnProperty(p)');
      return false;
    } else if (typeof nextProps[p] !== typeof prevProps[p]) {
      console.log('typeof nextProps[p] !== typeof prevProps[p]');
      return false;
    }
  }
  // console.log('p in prevProps');
  // 遍歷上次的屬性對象,優(yōu)先比較不相等的情形
  for (p in prevProps) {
    // 是否都存在某個(gè)屬性值
    if (nextProps.hasOwnProperty(p) !== prevProps.hasOwnProperty(p)) {
      console.log('nextProps.hasOwnProperty(p) !== prevProps.hasOwnProperty(p)');
      return false;
    }
    // 屬性值的類(lèi)型是否相等
    else if (typeof nextProps[p] !== typeof prevProps[p]) {
      console.log('typeof nextProps[p] !== typeof prevProps[p]');
      return false;
    }

    console.log('typeof prevProps[p]', typeof prevProps[p]);
    switch (typeof prevProps[p]) {
      // 對象類(lèi)型和函數類(lèi)型的處理
      case 'object':
      case 'function':
        leftChain.push(prevProps);
        rightChain.push(nextProps);

        if (!compare2Objects(prevProps[p], nextProps[p], leftChain, rightChain)) {
          console.log('!compare2Objects(prevProps[p], nextProps[p], leftChain, rightChain)');
          return false;
        }

        leftChain.pop();
        rightChain.pop();
        break;

      default:
        // 基礎類(lèi)型的處理
        if (prevProps[p] !== nextProps[p]) {
          return false;
        }
        break;
    }
  }

  return true;
}

3.用useCallback對組件方法進(jìn)行包裝

當父組件傳遞方法給子組件的時(shí)候,memo好像沒(méi)什么效果,無(wú)論是用const定義的方法,還在用箭頭函數或者bind定義的方法,子組件還是執行了

import React, { useState,memo } from 'react';
//子組件會(huì )有不必要渲染的例子
interface ChildProps {
  changeName: ()=>void;
}
const FunChild = ({ changeName}: ChildProps): JSX.Element => {
  console.log('普通函數子組件')
  return(
      <>
          <div>我是普通函數子組件</div>
          <button onClick={changeName}>普通函數子組件按鈕</button>
      </>
  );
}
const FunMemo = memo(FunChild);

const ArrowChild = ({ changeName}: ChildProps): JSX.Element => {
  console.log('箭頭函數子組件')
  return(
      <>
          <div>我是箭頭函數子組件</div>
          <button onClick={changeName.bind(null,'test')}>箭頭函數子組件按鈕</button>
      </>
  );
}
const ArrowMemo = memo(ArrowChild);

const BindChild = ({ changeName}: ChildProps): JSX.Element => {
  console.log('Bind函數子組件')
  return(
      <>
          <div>我是Bind函數子組件</div>
          <button onClick={changeName}>Bind函數子組件按鈕</button>
      </>
  );
}
const BindMemo = memo(BindChild);

const Page = (props:any) => {
  const [count, setCount] = useState(0);
  const name = "test";

  const changeName = function() {
    console.log('測試給子組件傳遞方法,使用useCallback后,子組件是否還會(huì )進(jìn)行無(wú)效渲染');
  }

  return (
      <>
          <button onClick={(e) => { setCount(count+1) }}>加1</button>
          <p>count:{count}</p>
          <ArrowMemo  changeName={()=>changeName()}/>
          <BindMemo  changeName={changeName.bind(null)}/>
          <FunMemo changeName={changeName} />
      </>
  )
}

export default Page;

使用useCallback,參數為[],頁(yè)面初始渲染后,改變count的值,傳遞普通函數的子組件不再渲染, 傳遞箭頭函數和bind方式書(shū)寫(xiě)的方法的子組件還是會(huì )渲染

import React, { useState,memo ,useCallback} from 'react';
//子組件會(huì )有不必要渲染的例子
interface ChildProps {
  changeName: ()=>void;
}
const FunChild = ({ changeName}: ChildProps): JSX.Element => {
  console.log('普通函數子組件')
  return(
      <>
          <div>我是普通函數子組件</div>
          <button onClick={changeName}>普通函數子組件按鈕</button>
      </>
  );
}
const FunMemo = memo(FunChild);

const ArrowChild = ({ changeName}: ChildProps): JSX.Element => {
  console.log('箭頭函數子組件')
  return(
      <>
          <div>我是箭頭函數子組件</div>
          <button onClick={changeName.bind(null,'test')}>箭頭函數子組件按鈕</button>
      </>
  );
}
const ArrowMemo = memo(ArrowChild);

const BindChild = ({ changeName}: ChildProps): JSX.Element => {
  console.log('Bind函數子組件')
  return(
      <>
          <div>我是Bind函數子組件</div>
          <button onClick={changeName}>Bind函數子組件按鈕</button>
      </>
  );
}
const BindMemo = memo(BindChild);

const Page = (props:any) => {
  const [count, setCount] = useState(0);
  const name = "test";

  const changeName = useCallback(() => {
    console.log('測試給子組件傳遞方法,使用useCallback后,子組件是否還會(huì )進(jìn)行無(wú)效渲染');
  },[])

  return (
      <>
          <button onClick={(e) => { setCount(count+1) }}>加1</button>
          <p>count:{count}</p>
          <ArrowMemo  changeName={()=>changeName()}/>
          <BindMemo  changeName={changeName.bind(null)}/>
          <FunMemo changeName={changeName} />
      </>
  )
}

export default Page;

4.用useMemo對組件中的對象變量進(jìn)行包裝

在子組件使用了memo,useCallback的情況下,給子組件傳遞一個(gè)對象屬性,對象值和方法都未發(fā)生改變的情況下,父組件無(wú)關(guān)狀態(tài)變更,子組件也會(huì )重新渲染。

import React, { useState,memo ,useCallback} from 'react';
//子組件會(huì )有不必要渲染的例子-使用了memo,useCallback的情況下,給子組件傳遞一個(gè)對象屬性值
interface ChildProps {
  childStyle: { color: string; fontSize: string;};
  changeName: ()=>void;
}
const FunChild = ({ childStyle,changeName}: ChildProps): JSX.Element => {
  console.log('普通函數子組件')
  return(
      <>
          <div style={childStyle}>我是普通函數子組件</div>
          <button onClick={changeName}>普通函數子組件按鈕</button>
      </>
  );
}
const FunMemo = memo(FunChild);

const Page = (props:any) => {
  const [count, setCount] = useState(0);
  const childStyle = {color:'green',fontSize:'16px'};

  const changeName = useCallback(() => {
    console.log('測試給子組件傳遞方法,使用useCallback后,子組件是否還會(huì )進(jìn)行無(wú)效渲染');
  },[])

  return (
      <>
          <button onClick={(e) => { setCount(count+1) }}>加1</button>
          <p>count:{count}</p>
          <FunMemo childStyle={childStyle} changeName={changeName} />
      </>
  )
}

export default Page;

使用useMemo可以解決給子組件傳遞對象屬性時(shí)的不必要更新問(wèn)題。

import React, { useState,memo, useMemo, useCallback} from 'react';
//子組件會(huì )有不必要渲染的例子
interface ChildProps {
  childStyle: { color: string; fontSize: string;};
  changeName: ()=>void;
}
const FunChild = ({ childStyle,changeName}: ChildProps): JSX.Element => {
  console.log('普通函數子組件')
  return(
      <>
          <div style={childStyle}>我是普通函數子組件</div>
          <button onClick={changeName}>普通函數子組件按鈕</button>
      </>
  );
}
const FunMemo = memo(FunChild);

const Page = (props:any) => {
  const [count, setCount] = useState(0);
  const [name, setName] = useState("");
  const childStyle = {color:'green',fontSize:'16px'};

  const changeName = useCallback(() => {
    setName('變一下名稱(chēng)')
  }, [])
  const childStyleMemo = useMemo(() => {
    return {
      color: name === '變一下名稱(chēng)' ? 'red':'green',
      fontSize: '16px'
    }
  }, [name])

  return (
      <>
          <button onClick={(e) => { setCount(count+1) }}>加1</button>
          <p>count:{count}</p>
          <FunMemo childStyle={childStyleMemo} changeName={changeName} />
      </>
  )
}

export default Page;

以上就是React Hooks使用避坑指南的詳細內容,更多關(guān)于React Hooks使用的資料請關(guān)注腳本之家其它相關(guān)文章!

免責聲明:本站發(fā)布的內容(圖片、視頻和文字)以原創(chuàng )、來(lái)自本網(wǎng)站內容采集于網(wǎng)絡(luò )互聯(lián)網(wǎng)轉載等其它媒體和分享為主,內容觀(guān)點(diǎn)不代表本網(wǎng)站立場(chǎng),如侵犯了原作者的版權,請告知一經(jīng)查實(shí),將立刻刪除涉嫌侵權內容,聯(lián)系我們QQ:712375056,同時(shí)歡迎投稿傳遞力量。

久久成人国产精品免费软件| 国产猛烈高潮尖叫视频免费| 被黑人猛躁10次高潮视频| 久久人人爽人人爽人人片AV高请| 免费AV一区二区三区无码| 欧美牲交黑粗硬大|