Reactが状態を更新する方法に関するメモ





良い一日、友達!



useState()フックは、React機能コンポーネントの状態を管理します。クラスコンポーネントでは、状態はthis.stateに格納され、this.setState()メソッドが呼び出されて更新されます。



通常、州での作業は難しいことではありません。ただし、更新に関連する重要なニュアンスが1つあります。



状態はどのように更新されますか:即時(同期)または延期(非同期)?答えを見つけるために読んでください。



1. useState()で状態を更新します



そのような機能コンポーネントがあるとしましょう:



import { useState } from 'react'

function DoubleIncreaser() {
  const [count, setCount] = useState(0)

  const doubleIncreaseHandler = () => {
    setCount(count + 1)
    setCount(count + 1)
  }

  return (
    <>
      <button onClick={doubleIncreaseHandler}>
        Double Increase
      </button>
      <div>Count: {count}</div>
    </>
  )
}

      
      





const [count、setCount] = useState(0)は、コンポーネントの初期状態を定義します。 countは現在の状態を含む変数であり、setCountはこの状態を更新する関数です。



コンポーネントには、倍増ボタンが含まれています。このボタンがクリックされると、doubleIncreaseHandlerハンドラーが呼び出され、カウントするために2つの連続した更新が実行されます。setCount(カウント+1)とsetCount(カウント+1)です。



ボタン1または2をクリックした後のコンポーネントの状態はどうなりますか?



この デモを開き、ボタンをクリックします。カウント値は、クリックするたびに1ずつ増加します。



setCount(count + 1)が状態を更新しても、カウント値はすぐには変更されません。代わりに、Reactは状態が更新されるようにスケジュールし、次にレンダリングされるときに、const [count、setCount] = useState(0)で、フックはカウントに新しい値を割り当てます。



例:変数countの値が0の場合、setCount(count + 1);を呼び出します。 setCount(count + 1)はsetCount(0 + 1)に評価されます。 setCount(0 + 1)-次のレンダリングの状態値として1になります。



したがって、[value、setValue] = useState()でsetValue(newValue)を使用して状態を更新することは、非同期で行われます。



ただし、状態更新関数は、現在の状態に基づいて新しい状態を計算するための引数としてコールバックを受け取ることができます。この場合、setCount(actualCount => actualCount + 1)を使用できます。



import { useState } from 'react'

function DoubleIncreaser() {
  const [count, setCount] = useState(0)

  const doubleIncreaseHandler = () => {
    setCount(actualCount => actualCount + 1)
    setCount(actualCount => actualCount + 1)
  }

  return (
    <>
      <button onClick={doubleIncreaseHandler}>
        Double Increase
      </button>
      <div>Count: {count}</div>
    </>
  )
}

      
      





この関数を使用して状態を更新する場合、actualCount引数には実際の状態値が含まれます。



この デモを開き、ボタンをクリックします。予想通り、カウントは2に増えます。



もちろん、いつでも中間変数を作成できます。



import { useState } from 'react'

function DoubleIncreaser() {
  const [count, setCount] = useState(0)

  const doubleIncrease = () => {
    let actualCount = count
    actualCount = actualCount + 1
    actualCount = actualCount + 1
    setCount(actualCount)
  }

  return (
    <>
      <button onClick={this.doubleIncrease}>
        Double Increase
      </button>
      <div>Count: {count}</div>
    </>
  )
}

      
      





actualCount = countは、必要に応じて更新できる中間変数です。この変数は、setCount(actualCount)を使用して状態を更新するために使用されます。



2.状態は不変(不変)で読み取り専用です



次のレンダリングで状態が更新されることを忘れた場合は、値を変更した直後に値の読み取りを試みることができます。残念ながら、何も得られません。



function FetchUsers() {
  const [users, setUsers] = useState([])

  useEffect(() => {
    const startFetching = async () => {
      const response = await fetch('/users')
      const fetchedUsers = await response.json()

      setUsers(fetchedUsers)
      console.log(users)        // => []
      console.log(fetchedUsers) // => ['John', 'Jane', 'Alice', 'Bob']
    }
    startFetching()
  }, [])

  return (
    <ul>
      {users.map(user => <li>{user}</li>)}
    </ul>
  )
}

      
      





FetchUsersコンポーネントは、マウント要求を送信します-startFetching()。



データを受信すると、setUsers(fetchedUsers)が状態を更新します。ただし、変更は一夜にして行われません。



users変数は不変で、読み取り専用です。useState()フックのみが新しい値を割り当てることができます。これを直接行うことはできません。



  function FetchUsers() {
    const [users, setUsers] = useState([])

    useEffect(() => {
      const startFetching = async () => {
        const response = await fetch('/users')
        const fetchedUsers = await response.json()

        users = fetchedUsers       // ! users    
        users.push(...fetchedUsers) // ! users 
        setUsers(fetchedUsers)     // !
      }
      startFetching()
    }, [])

    return (
      <ul>
        {users.map(user => <li>{user}</li>)}
      </ul>
    )
  }

      
      





3.クラスコンポーネントの状態を更新します



非同期状態の更新は、クラスコンポーネントで一般的です。



例を考えてみましょう:



import { Component } from 'react';

class DoubleIncreaser extends Component {
  state = {
    count: 0
  };

  render() {
    return (
      <>
        <button onClick={this.doubleIncrease}>
          Double Increase
        </button>
        <div>Count: {this.state.count}</div>
      </>
    );
  }

  doubleIncrease = () => {
    // !
    this.setState(({ count }) => ({
      count: count + 1
    }));
    this.setState(({ count }) => ({
      count: count + 1
    }));

    //  !
    // this.setState({ count: this.state.count + 1 });
    // this.setState({ count: this.state.count + 1 });
  }
}

      
      





doubleIncrease()ハンドラーに注意してください。コールバック関数を使用して状態を更新します。



この デモを開き、ボタンをクリックします。this.stateの値は2に増加します。



クラスコンポーネントでは、this.stateもすぐには更新されません。this.setState(newState)が呼び出されると、Reactは次のレンダリングまでthis.stateの更新を延期します。



したがって、this.setState(newState)はthis.stateを非同期的に更新します。



4.結論



useState()フックとthis.setState()(クラスコンポーネント内)は、変数値とコンポーネントの状態を非同期に更新します。



単純なルールを覚えておいてください。useState()(またはthis.setState())フックのsetState(newValue)セッターを呼び出すと、状態はすぐには更新されませんが、コンポーネントの次のレンダリング時に更新されます。



Reactを(index.jsに)一度だけインポートする必要があることに気づきましたか?これは、コンポーネントでは不要になりました。



ご清聴ありがとうございました。良い一日を。



All Articles