useState
UI ツリーで見たときに同じ階層にあるが、別の位置にあると別のステートとして扱われる。
UIツリーとは?
ReactがReact DOMで扱いやすいような形にJSXを変形して渡すときの構造である。(以下: UIツリー)
ステートがリセットされるタイミング
- 要素が削除されるとき
import { useState } from 'react';
export default function App() {
  const [showB, setShowB] = useState(true);
  return (
    <div>
      <Counter />
      ## 注目
      {showB && <Counter />} 
      <label>
        <input
          type="checkbox"
          checked={showB}
          onChange={e => {
            setShowB(e.target.checked)
          }}
        />
        Render the second counter
      </label>
    </div>
  );
}
function Counter() {
  const [score, setScore] = useState(0);
  const [hover, setHover] = useState(false);
  let className = 'counter';
  if (hover) {
    className += ' hover';
  }
  return (
    <div
      className={className}
      onPointerEnter={() => setHover(true)}
      onPointerLeave={() => setHover(false)}
    >
      <h1>{score}</h1>
      <button onClick={() => setScore(score + 1)}>
        Add one
      </button>
    </div>
  );
}
上記のコードの一部に{showB && <Counter />}と書いている部分がある、これはshowBというステートがあり、そのステートが True になると<Counter />が生成される。
しかしshowBが False になると<Counter />が削除される。その削除されるタイミングでステートがリセットされる。
以下参考画像。
逆にステートが保持される使い方
UIツリー上での位置が同じタイミング。
import { useState } from 'react';
export default function App() {
  const [isFancy, setIsFancy] = useState(false);
  if (isFancy) {
    return (
      <div>
        <Counter isFancy={true} />
        <label>
          <input
            type="checkbox"
            checked={isFancy}
            onChange={e => {
              setIsFancy(e.target.checked)
            }}
          />
          Use fancy styling
        </label>
      </div>
    );
  }
  return (
    <div>
      <Counter isFancy={false} />
      <label>
        <input
          type="checkbox"
          checked={isFancy}
          onChange={e => {
            setIsFancy(e.target.checked)
          }}
        />
        Use fancy styling
      </label>
    </div>
  );
}
function Counter({ isFancy }) {
  const [score, setScore] = useState(0);
  const [hover, setHover] = useState(false);
  let className = 'counter';
  if (hover) {
    className += ' hover';
  }
  if (isFancy) {
    className += ' fancy';
  }
  return (
    <div
      className={className}
      onPointerEnter={() => setHover(true)}
      onPointerLeave={() => setHover(false)}
    >
      <h1>{score}</h1>
      <button onClick={() => setScore(score + 1)}>
        Add one
      </button>
    </div>
  );
}
上記のコードでinput:checkboxに注目していただくと、2つとも階層がdiv > label > input
にあると思います。
上記のように同じ階層だとステートの値は保持されます。
問題
Q1. 以下のコードでステートは削除されるか。
import { useState } from 'react';
export default function MyComponent() {
  const [counter, setCounter] = useState(0);
  function MyTextField() {
    const [text, setText] = useState('');
    return (
      <input
        value={text}
        onChange={e => setText(e.target.value)}
      />
    );
  }
  return (
    <>
      <MyTextField />
      <button onClick={() => {
        setCounter(counter + 1)
      }}>Clicked {counter} times</button>
    </>
  );
}
解答を見る
ステートは削除される
Q2. 以下のコードでステートは別々に扱われるか。
import { useState } from 'react';
export default function Scoreboard() {
  const [isPlayerA, setIsPlayerA] = useState(true);
  return (
    <div>
      {isPlayerA &&
        <Counter person="Taylor" />
      }
      {!isPlayerA &&
        <Counter person="Sarah" />
      }
      <button onClick={() => {
        setIsPlayerA(!isPlayerA);
      }}>
        Next player!
      </button>
    </div>
  );
}
function Counter({ person }) {
  const [score, setScore] = useState(0);
  const [hover, setHover] = useState(false);
  let className = 'counter';
  if (hover) {
    className += ' hover';
  }
  return (
    <div
      className={className}
      onPointerEnter={() => setHover(true)}
      onPointerLeave={() => setHover(false)}
    >
      <h1>{person}'s score: {score}</h1>
      <button onClick={() => setScore(score + 1)}>
        Add one
      </button>
    </div>
  );
}
解答を見る
別々に扱われる


