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

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

青灯夜游
リリース: 2022-03-22 12:12:22
オリジナル
8416 人が閲覧しました

相違点: 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 (
      <div>
        <p>you click { count }</p>
        <button onClick={this.handleClick}>点击</button>
      </div>
    )
  }
}
ログイン後にコピー

フック

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 &#39;react&#39;
import Child from &#39;./child&#39;

export const MyContext = createContext()

export default function Parent() {

  return (
    <div>
      <p>Parent</p>
      <MyContext.Provider value={{name: &#39;cc&#39;, age: 21}}>
        <Child />
      </MyContext.Provider>
    </div>
  )
}
ログイン後にコピー

子组件

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提供值:<MyContext.provide value={{name: &#39;cc&#39;, age: 22}} />
  • 子组件导入父组件的context:import { MyContext } from &#39;./parent&#39;
  • 获取父组件提供的值: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 サイトの他の関連記事を参照してください。

関連ラベル:
ソース:php.cn
このウェブサイトの声明
この記事の内容はネチズンが自主的に寄稿したものであり、著作権は原著者に帰属します。このサイトは、それに相当する法的責任を負いません。盗作または侵害の疑いのあるコンテンツを見つけた場合は、admin@php.cn までご連絡ください。
最新の問題
人気のチュートリアル
詳細>
関連するチュートリアル
人気のおすすめ
最新のコース
最新のダウンロード
詳細>
ウェブエフェクト
公式サイト
サイト素材
フロントエンドテンプレート