ホームページ  >  記事  >  ウェブフロントエンド  >  反応フックとクラスの違いは何ですか?

反応フックとクラスの違いは何ですか?

青灯夜游
青灯夜游オリジナル
2022-03-22 12:12:228359ブラウズ

相違点: 1. フックはクラスよりも簡潔に記述されます; 2. フックのビジネス コードはクラスよりも集約されています; 3. クラス コンポーネントのロジックの再利用では通常、レンダリング プロパティと HOC が使用されますが、反応フックは使用を提供しますロジックを再利用するためのカスタム フック。

反応フックとクラスの違いは何ですか?

このチュートリアルの動作環境: Windows7 システム、react17.0.1 バージョン、Dell G3 コンピューター。

反応フックとクラスコンポーネントの違いは何ですか?反応フックとクラスコンポーネントを比較し、それらの違いについて話しましょう。

react-hooks によって解決される問題

  • 関数コンポーネントは独自の状態を持つことができません。フックが登場する前は、関数コンポーネントはステートレスであり、親コンポーネントの状態は props を通じて取得されていましたが、フックでは関数コンポーネントの内部状態を維持するための useState が提供されます。

  • コンポーネントのライフサイクルは、関数コンポーネントでは監視できません。 useEffect は複数のライフサイクル関数を集約します。

  • クラス コンポーネントのライフ サイクルはより複雑です (バージョン 15 からバージョン 16 への変更は大きくなります)。

  • クラス コンポーネント ロジックは再利用が困難です (HOC、レンダリング プロップ)。

クラスと比較したフックの利点 (比較)

1. 記述方法がより簡潔になります

私たちは以下を使用します最も単純なカウンター 例:

クラスコンポーネント

class ExampleOfClass extends Component {
  constructor(props) {
    super(props)
    this.state = {
      count: 1
    }
  }
  handleClick = () => {
    let { count } = this.state
    this.setState({
      count: count+1
    })
  }
  render() {
    const { count } = this.state
    return (
      dc6dce4a544fdca2df29d5ac0ea9906b
        e388a4556c0f65e1904146cc1a846beeyou click { count }94b3e26ee717c64999d7867364b1b4a3
        58d87a863b0fd08858af212a24752208点击65281c5ac262bf6d81768915a4a77ac0
      16b28748ea4df4d9c2150843fecfba68
    )
  }
}

フック

function ExampleOfHooks() {
    const [count, setCount] = useState(0)
    const handleClick = () => {
        setCount(count + 1)
    }
    return (
      <div>
        <p>you click { count }</p>
        <button onClick={handleClick}>点击</button>
      </div>
    )
}

フックを使用したコードは次のとおりであることがわかります。クラスコンポーネントコードと比較して、より簡潔で明確です。

2. ビジネス コードはより集約されています

クラス コンポーネントを使用する場合、関数が 2 つのライフ サイクル関数に含まれることがよくあります。これらを別々に記述すると、場合によっては問題が発生する可能性があります。問題は忘れてください。例:

let timer = null
componentDidMount() {
    timer = setInterval(() => {
        // ...
    }, 1000)
}
// ...
componentWillUnmount() {
    if (timer) clearInterval(timer)
}

タイマーの追加とタイマーのクリアは 2 つの異なるライフサイクル関数に含まれるため、間に他の多くのビジネス コードが存在する可能性があるため、コンポーネントが次の場合にタイマーのクリアを忘れる可能性があります。クリアタイマー機能を追加しないと、メモリリークや継続的なネットワークリクエストなどの問題が発生する可能性があります。

しかし、フックを使用すると、コードがより集中化され、管理しやすくなり、忘れにくくなります:

useEffect(() => {
    let timer = setInterval(() => {
        // ...
    }, 1000)
    return () => {
        if (timer) clearInterval(timer)
    }
}, [//...])

3. 便利なロジックの再利用

クラス コンポーネントのロジック再利用では、通常、レンダー プロパティと HOC が使用されます。 React フックは、ロジックを再利用するためのカスタム フックを提供します。

以下では、例としてページ上のマウス位置を取得するロジックの再利用を取り上げています。

クラス コンポーネントのレンダー プロパティ メソッドの再利用

import React, { Component } from &#39;react&#39;

class MousePosition extends Component {
  constructor(props) {
    super(props)
    this.state = {
      x: 0,
      y: 0
    }
  }

  handleMouseMove = (e) => {
    const { clientX, clientY } = e
    this.setState({
      x: clientX,
      y: clientY
    })
  }

  componentDidMount() {
    document.addEventListener(&#39;mousemove&#39;, this.handleMouseMove)
  }

  componentWillUnmount() {
    document.removeEventListener(&#39;mousemove&#39;, this.handleMouseMove)
  }

  render() {
    const { children } = this.props
    const { x, y } = this.state
    return(
      <div>
        {
          children({x, y})
        }
      </div>
    )
  }

}

// 使用
class Index extends Component {
  constructor(props) {
    super(props)
  }

  render() {
    return (
      <MousePosition>
        {
          ({x, y}) => {
            return (
              <div>
                <p>x:{x}, y: {y}</p>
              </div>
            )
          }
        }
      </MousePosition>
    )
  }
}

export default Index

カスタム フック メソッドの再利用

import React, { useEffect, useState } from &#39;react&#39;

function usePosition() {
  const [x, setX] = useState(0)
  const [y, setY] = useState(0)

  const handleMouseMove = (e) => {
    const { clientX, clientY } = e
    setX(clientX)
    setY(clientY)
  } 

  useEffect(() => {
    document.addEventListener(&#39;mousemove&#39;, handleMouseMove)
    return () => {
      document.removeEventListener(&#39;mousemove&#39;, handleMouseMove)
    }
  })
  return [
    {x, y}
  ]
}

// 使用
function Index() {
  const [position] = usePosition()
  return(
    <div>
      <p>x:{position.x},y:{position.y}</p>
    </div>
  )
}

export default Index

フックを使用すると、ロジックの再利用がより便利になり、使用するとロジックがより明確になることが明らかです。

#フックの一般的な API の使用法

1、useState

#Syntax

const [value, setValue] = useState(0)

この構文は ES6 配列構造です。配列の最初の値は宣言された状態で、2 番目の値は状態変化機能。

各フレームは独立した状態を持つ

私の個人的な理解では、各フレームの独立した状態はクロージャー メソッドを使用して実現されます。
function Example() {
  const [val, setVal] = useState(0)
  const timeoutFn = () => {
      setTimeout(() => {
        // 取得的值是点击按钮的状态,不是最新的状态
          console.log(val)
      }, 1000)
  }
  return (
      <>
          <p>{val}</p>
          <button onClick={()=>setVal(val+1)}>+</button>
          <button onClick={timeoutFn}>alertNumber</button>
      </>
  )
}

コンポーネントのステータスまたはプロパティが更新されると、関数コンポーネントはレンダリングのために再呼び出されます。各レンダリングは独立しており、独自の独立したプロパティと状態を持ち、他のレンダリングには影響しません。

2. useEffect

Syntax

useEffect(() => {
    //handler function...
    
    return () => {
        // clean side effect
    }
}, [//dep...])
useEffect は、依存関係が変更されたときにコールバック関数と依存関係を受け取ります。内部のコールバック関数が実行されます。 useEffect は、クラス コンポーネント DidMount、didUpdate、および willUnmount のライフサイクル関数に似ています。

    useEffect は非同期であり、コンポーネントがレンダリングされるまで実行されません
  • useEffect のコールバック関数は、副作用をクリアするか、返さない処理関数のみを返すことができます。
  • useEffect によって渡された依存関係が空の配列の場合、useEffect 内の関数は1 回実行
3. useMemo、useCallback

useMemo および useCallback は主に、コンポーネントの更新数を減らし、コンポーネントのパフォーマンスを最適化するために使用されます。

    useMemo はコールバック関数と依存関係を受け取り、コールバック関数は依存関係が変更された場合にのみ再実行されます。
  • useCallback は、コールバック関数と依存関係を受け取り、コールバック関数の記憶されたバージョンを返します。記憶されたバージョンは、依存関係が変更された場合にのみ再記憶されます。
構文

const memoDate = useMemo(() => data, [//dep...])
const memoCb = useCallback(() => {//...}, [//dep...])
コンポーネントのパフォーマンスを最適化するときは、通常、クラス コンポーネントに React.PureComponent を使用します。PureComponent は shouldUpdate で比較を実行します。更新が必要かどうか; 関数コンポーネントには通常 React.memo を使用します。ただし、react フックを使用すると、各レンダリング更新が独立している (新しい状態が生成される) ため、React.memo を使用した場合でも、再レンダリングが行われます。

比如下面这种场景,改变子组件的name值后由于父组件更新后每次都会生成新值(addAge函数会改变),所以子组件也会重新渲染。

function Parent() {
  const [name, setName] = useState(&#39;cc&#39;)
  const [age, setAge] = useState(22)

  const addAge = () => {
    setAge(age + 1)
  }

  return (
    <>
      <p>父组件</p>
      <input value={name} onChange={(e) => setName(e.target.value)} />
      <p>age: {age}</p>
      <p>-------------------------</p>
      <Child addAge={addAge} />
    </>
  )
}

const Child = memo((props) => {
  const { addAge } = props
  console.log(&#39;child component update&#39;)
  return (
    <>
      <p>子组件</p>
      <button onClick={addAge}>click</button>
    </>
  )
})

使用useCallback优化

function Parent() {
  const [name, setName] = useState(&#39;cc&#39;)
  const [age, setAge] = useState(22)

  const addAge = useCallback(() => {
    setAge(age + 1)
  }, [age])

  return (
    <>
      <p>父组件</p>
      <input value={name} onChange={(e) => setName(e.target.value)} />
      <p>age: {age}</p>
      <p>-------------------------</p>
      <Child addAge={addAge} />
    </>
  )
}

const Child = memo((props) => {
  const { addAge } = props
  console.log(&#39;child component update&#39;)
  return (
    <>
      <p>子组件</p>
      <button onClick={addAge}>click</button>
    </>
  )
})

只有useCallback的依赖性发生变化时,才会重新生成memorize函数。所以当改变name的状态是addAge不会变化。

4、useRef

useRef类似于react.createRef。

const node = useRef(initRef)

useRef 返回一个可变的 ref 对象,其 current 属性被初始化为传入的参数(initRef)

作用在DOM上

const node = useRef(null)
<input ref={node} />

这样可以通过node.current属性访问到该DOM元素。

需要注意的是useRef创建的对象在组件的整个生命周期内保持不变,也就是说每次重新渲染函数组件时,返回的ref 对象都是同一个(使用 React.createRef ,每次重新渲染组件都会重新创建 ref)。

5、useReducer

useReducer类似于redux中的reducer。

语法

const [state, dispatch] = useReducer(reducer, initstate)

useReducer传入一个计算函数和初始化state,类似于redux。通过返回的state我们可以访问状态,通过dispatch可以对状态作修改。

const initstate = 0;
function reducer(state, action) {
  switch (action.type) {
    case &#39;increment&#39;:
      return {number: state.number + 1};
    case &#39;decrement&#39;:
      return {number: state.number - 1};
    default:
      throw new Error();
  }
}
function Counter(){
    const [state, dispatch] = useReducer(reducer, initstate);
    return (
        <>
          Count: {state.number}
          <button onClick={() => dispatch({type: &#39;increment&#39;})}>+</button>
          <button onClick={() => dispatch({type: &#39;decrement&#39;})}>-</button>
        </>
    )
}

6、useContext

通过useContext我们可以更加方便的获取上层组件提供的context。

父组件

import React, { createContext, Children } from 'react'
import Child from './child'

export const MyContext = createContext()

export default function Parent() {

  return (
    dc6dce4a544fdca2df29d5ac0ea9906b
      e388a4556c0f65e1904146cc1a846beeParent94b3e26ee717c64999d7867364b1b4a3
      6c114b46cb8836ff0ac83d93f4259369
        b709c9e9a1f23de0af93c9c27ea4dcef
      8e66e6aff1f0a13ebced51b2c1b5d182
    16b28748ea4df4d9c2150843fecfba68
  )
}

子组件

import React, { useContext } from &#39;react&#39;
import { MyContext } from &#39;./parent&#39;

export default function Parent() {
  const data = useContext(MyContext) // 获取父组件提供的context
  console.log(data)
  return (
    <div>
      <p>Child</p>
    </div>
  )
}

使用步骤

  • 父组件创建并导出context:export const MyContext = createContext()
  • 父组件使用providervalue提供值:48721ce1b4e79a5bb092f89212a3882a
  • 子组件导入父组件的context:import { MyContext } from './parent'
  • 获取父组件提供的值:const data = useContext(MyContext)

不过在多数情况下我们都不建议使用context,因为会增加组件的耦合性。

7、useLayoutEffect

useEffect 在全部渲染完毕后才会执行;useLayoutEffect 会在 浏览器 layout之后,painting之前执行,并且会柱塞DOM;可以使用它来读取 DOM 布局并同步触发重渲染。

export default function LayoutEffect() {
  const [color, setColor] = useState(&#39;red&#39;)
  useLayoutEffect(() => {
      alert(color) // 会阻塞DOM的渲染
  });
  useEffect(() => {
      alert(color) // 不会阻塞
  })
  return (
      <>
        <div id="myDiv" style={{ background: color }}>颜色</div>
        <button onClick={() => setColor(&#39;red&#39;)}>红</button>
        <button onClick={() => setColor(&#39;yellow&#39;)}>黄</button>
      </>
  )
}

上面的例子中useLayoutEffect会在painting之前执行,useEffect在painting之后执行。

hooks让函数组件拥有了内部状态、生命周期,使用hooks让代码更加的简介,自定义hooks方便了对逻辑的复用,并且摆脱了class组件的this问题;但是在使用hooks时会产生一些闭包问题,需要仔细使用。

【相关推荐:Redis视频教程

以上が反応フックとクラスの違いは何ですか?の詳細内容です。詳細については、PHP 中国語 Web サイトの他の関連記事を参照してください。

声明:
この記事の内容はネチズンが自主的に寄稿したものであり、著作権は原著者に帰属します。このサイトは、それに相当する法的責任を負いません。盗作または侵害の疑いのあるコンテンツを見つけた場合は、admin@php.cn までご連絡ください。