【Reactクイズ】stateは保持される?リセットされる?
全部で10問です。
以降の問の中で各Counterはstateを持っています。チェックボックスでCounterコンポーネントの表示切替をしたときのstateの状態を当てるクイズになります。もしコンポーネントの表示を切り替えをしてもstateが保持される場合は「保持される」、リセットされる場合は「リセットされる」と回答してください。
↓画面イメージ
バージョン
"react": "^18.3.1",
"react-dom": "^18.3.1",
問1
import { useState } from 'react';
export default function App() {
const [isRed, setIsRed] = useState(false);
return (
<div>
{isRed ? <Counter isRed={true} /> : <Counter isRed={false} />}
<input
type="checkbox"
checked={isRed}
onChange={(e) => {
setIsRed(e.target.checked);
}}
/>
</div>
);
}
function Counter({ isRed }: { isRed: boolean }) {
const [count, setCount] = useState(0);
const style = {
color: isRed ? 'red' : undefined,
};
return (
<div>
<h1 style={style}>{count}</h1>
<button onClick={() => setCount(count + 1)}>countup</button>
</div>
);
}
答え
保持される
Appから見たCounterの位置に変化がないから
問2
import { useState } from 'react';
export default function App() {
const [isRed, setIsRed] = useState(false);
return (
<div>
{isRed ? <Counter isRed={true} /> : null}
<input
type="checkbox"
checked={isRed}
onChange={(e) => {
setIsRed(e.target.checked);
}}
/>
</div>
);
}
function Counter({ isRed }: { isRed: boolean }) {
const [count, setCount] = useState(0);
const style = {
color: isRed ? 'red' : undefined,
};
return (
<div>
<h1 style={style}>{count}</h1>
<button onClick={() => setCount(count + 1)}>countup</button>
</div>
);
}
答え
リセットされる
Counterコンポーネントがアンマウントされるから
問3
import { useState } from 'react';
export default function App() {
const [isRed, setIsRed] = useState(false);
return (
<div>
{isRed ? <Counter isRed={true} /> : <Counter isRed={false} />}
<input
type="checkbox"
checked={isRed}
onChange={(e) => {
setIsRed(e.target.checked);
}}
/>
</div>
);
}
function Counter({ isRed }: { isRed: boolean }) {
const [count, setCount] = useState(0);
const style = {
color: isRed ? 'red' : undefined,
};
if (isRed) {
return (
<div>
<h1 style={style}>{count}</h1>
<p>赤文字カウンター</p>
<button onClick={() => setCount(count + 1)}>countup</button>
</div>
);
} else {
return (
<div>
<h1 style={style}>{count}</h1>
{null}
<button onClick={() => setCount(count + 1)}>countup</button>
</div>
);
}
}
答え
保持される
Appから見たCounterの位置に変化がないから
問4
import { useState } from "react";
export default function App() {
const [isRed, setIsRed] = useState(false);
return (
<div>
{isRed ? <Counter isRed={true} /> : <Counter isRed={false} />}
<input
type="checkbox"
checked={isRed}
onChange={(e) => {
setIsRed(e.target.checked);
}}
/>
</div>
);
}
function Counter({ isRed }: { isRed: boolean }) {
const [count, setCount] = useState(0);
const style = {
color: isRed ? "red" : undefined,
};
if (isRed) {
return (
<div>
<p>赤文字カウンター</p>
<button onClick={() => setCount(count + 1)}>countup</button>
<h1 style={style}>{count}</h1>
</div>
);
} else {
return (
<section>
<h1 style={style}>{count}</h1>
<button onClick={() => setCount(count + 1)}>countup</button>
</section>
);
}
}
答え
保持される
Appから見たCounterの位置に変化がないから
問5
import { useState } from 'react';
export default function App() {
const [isRed, setIsRed] = useState(false);
return (
<div>
{isRed ? (
<div>
<Counter isRed={true} />
</div>
) : (
<section>
<Counter isRed={false} />
</section>
)}
<input
type="checkbox"
checked={isRed}
onChange={(e) => {
setIsRed(e.target.checked);
}}
/>
</div>
);
}
function Counter({ isRed }: { isRed: boolean }) {
const [count, setCount] = useState(0);
const style = {
color: isRed ? 'red' : undefined,
};
return (
<div>
<h1 style={style}>{count}</h1>
<button onClick={() => setCount(count + 1)}>countup</button>
</div>
);
}
答え
リセットされる
Appから見てdivがsectionに変化している場合、その要素の配下にある要素のコンポーネントは全てアンマウントされたあと再度レンダーされるから
問6
import { useState } from 'react';
export default function App() {
const [isRed, setIsRed] = useState(false);
return (
<div>
{isRed && <Counter isRed={true} />}
{!isRed && <Counter isRed={true} />}
<input
type="checkbox"
checked={isRed}
onChange={(e) => {
setIsRed(e.target.checked);
}}
/>
</div>
);
}
function Counter({ isRed }: { isRed: boolean }) {
const [count, setCount] = useState(0);
const style = {
color: isRed ? 'red' : undefined,
};
return (
<div>
<h1 style={style}>{count}</h1>
<button onClick={() => setCount(count + 1)}>countup</button>
</div>
);
}
答え
リセットされる
Appから見たCounterの位置が変化しているから
問7
import { useState } from 'react';
export default function App() {
const [isRed, setIsRed] = useState(false);
return (
<div>
{isRed ? (
<div>
<p>赤文字カウンター</p>
<Counter isRed={true} />
</div>
) : (
<div>
<Counter isRed={false} />
</div>
)}
<input
type="checkbox"
checked={isRed}
onChange={(e) => {
setIsRed(e.target.checked);
}}
/>
</div>
);
}
function Counter({ isRed }: { isRed: boolean }) {
const [count, setCount] = useState(0);
const style = {
color: isRed ? 'red' : undefined,
};
return (
<div>
<h1 style={style}>{count}</h1>
<button onClick={() => setCount(count + 1)}>countup</button>
</div>
);
}
答え
リセットされる
Appから見たCounterの位置が変化しているから
問8
import { useState } from 'react';
export default function App() {
const [isRed, setIsRed] = useState(false);
return (
<div>
{isRed ? (
<div>
<p>赤文字カウンター</p>
<Counter isRed={true} />
</div>
) : (
<div>
{null}
<Counter isRed={false} />
</div>
)}
<input
type="checkbox"
checked={isRed}
onChange={(e) => {
setIsRed(e.target.checked);
}}
/>
</div>
);
}
function Counter({ isRed }: { isRed: boolean }) {
const [count, setCount] = useState(0);
const style = {
color: isRed ? 'red' : undefined,
};
return (
<div>
<h1 style={style}>{count}</h1>
<button onClick={() => setCount(count + 1)}>countup</button>
</div>
);
}
答え
保持される
Appから見たCounterの位置が変化していないから
問9
import { useState } from 'react';
export default function App() {
const [isRed, setIsRed] = useState(false);
return (
<div>
{isRed ? <Counter key="red" isRed={true} /> : <Counter key="none" isRed={false} />}
<input
type="checkbox"
checked={isRed}
onChange={(e) => {
setIsRed(e.target.checked);
}}
/>
</div>
);
}
function Counter({ isRed }: { isRed: boolean }) {
const [count, setCount] = useState(0);
const style = {
color: isRed ? 'red' : undefined,
};
return (
<div>
<h1 style={style}>{count}</h1>
<button onClick={() => setCount(count + 1)}>countup</button>
</div>
);
}
答え
リセットされる
keyが異なるから
問10
姓と名のテキストフィールドとチェックボックスがあります。チェックボックスを押下することで1と2の表示を切り替えることができます。
1:lastNameが下、firstNameが上の表示
2:lastNameが上、firstNameが下の表示
各テキストフィールドそれぞれは入力をstateとして保持しています。
では表示を切り替えたとき状態は保持されますか?リセットされますか?
↓画面イメージ
import { useState } from 'react';
export default function App() {
const [reverse, setReverse] = useState(false);
const checkbox = (
<label>
<input type="checkbox" checked={reverse} onChange={(e) => setReverse(e.target.checked)} />
Reverse order
</label>
);
if (reverse) {
return (
<>
<Field key="lastName" label="Last name" />
<Field key="firstName" label="First name" />
{checkbox}
</>
);
} else {
return (
<>
<Field key="firstName" label="First name" />
<Field key="lastName" label="Last name" />
{checkbox}
</>
);
}
}
function Field({ label }: { label: string }) {
const [value, setValue] = useState('');
return (
<div>
<label>{label}</label>
<input value={value} onChange={(e) => setValue(e.target.value)} />
</div>
);
}
答え
保持される
補足としてkey propsを削除したとしても保持されます。ただしstateはkeyではなくコンポーネントの位置に紐づいているのでlastNameに値を入力したあと切り替えるとfirstNameにlastNameの値が入るので注意してください。
以上です。
ポイント
stateの保持ルール
stateはレンダーツリーの位置に保持されます。コンポーネントはstateを持っていません。コンポーネントの外でReactがstateを管理しています。
stateのリセットタイミング
- コンポーネントのアンマウント
- 親の要素の変化
- 位置の変化
- keyの変化
Discussion