RORK LABEN
TOOLING — Rorkの開発者向けリポジトリが動き続けています。rork-xcodeが7月16日、rork-deviceが7月15日、rork-plistが7月13日に更新されましたOPUS46 — RorkでClaude Opus 4.6が稼働しています。Rork MaxはClaude Codeを土台にアプリを組み立てる設計ですSIM — ブラウザ上で動くクラウドのiOSシミュレータを備え、実機へのインストールは1クリック、App Storeへの提出は2クリックと案内されていますMAX — Rork MaxはReact Nativeではなく純粋なSwiftを出力します。iPhone・iPad・Apple Watch・Apple TV・Vision Pro、そしてiMessageまでが射程ですNATIVE — HealthKit、ARKitとLiDAR、NFC、Dynamic Island、Live Activities、Metalによる3D、Core MLのオンデバイス推論まで扱えますSEED — RorkはLeft Lane Capitalが主導する1,500万ドルのシードラウンドを実施し、Peak XVとa16z Speedrunが参加しましたTOOLING — Rorkの開発者向けリポジトリが動き続けています。rork-xcodeが7月16日、rork-deviceが7月15日、rork-plistが7月13日に更新されましたOPUS46 — RorkでClaude Opus 4.6が稼働しています。Rork MaxはClaude Codeを土台にアプリを組み立てる設計ですSIM — ブラウザ上で動くクラウドのiOSシミュレータを備え、実機へのインストールは1クリック、App Storeへの提出は2クリックと案内されていますMAX — Rork MaxはReact Nativeではなく純粋なSwiftを出力します。iPhone・iPad・Apple Watch・Apple TV・Vision Pro、そしてiMessageまでが射程ですNATIVE — HealthKit、ARKitとLiDAR、NFC、Dynamic Island、Live Activities、Metalによる3D、Core MLのオンデバイス推論まで扱えますSEED — RorkはLeft Lane Capitalが主導する1,500万ドルのシードラウンドを実施し、Peak XVとa16z Speedrunが参加しました
記事一覧/開発ツール
開発ツール/2026-04-23中級

Rork で state を更新したのに画面が再描画されない原因と直し方

Rorkで生成したコードでsetStateしたのに画面が更新されない問題を5つの典型パターンで解説。配列のミューテート・古いクロージャ・Zustandの誤用など、再現性の高い原因を具体的なコード例で修正します。

state管理2再描画useState2Zustand4トラブルシューティング77Rork515React Native209

setState したのに画面が変わらない——Rork で生成したコードを触っていて、この壁にぶつかったことのある方は多いのではないでしょうか。console.log を挟むと値は確かに変わっているのに、UI だけが古いまま止まっています。無限ループの方がまだ分かりやすくて、この「何も起こらない」バグは原因が見えにくい分、時間を溶かしがちです。

私自身、Rork で試作したアプリで半日悩んだ末に「React は参照が同じなら再描画しない」という基本ルールに戻ってようやく気づいた、という経験があります。原因は実はいくつかのパターンに集約できますので、順番に確認していけば落ち着いて切り分けられます。

よくある原因1: 配列やオブジェクトを直接ミューテートしている

一番多いのがこれです。Rork に「配列の末尾にアイテムを追加して」と頼むと、たまに .push() を使ったコードが出てくることがあります。

// ❌ これは再描画されません
const [items, setItems] = useState<string[]>([]);
 
const addItem = (text: string) => {
  items.push(text);      // 既存配列を破壊
  setItems(items);       // 参照は同じまま
};

React は前回の state と今回の state を Object.is で比較します。items.push() は同じ配列オブジェクトを書き換えるので、参照が変わらず「変化なし」と判定されてしまうのです。

// ✅ 新しい配列を作って渡す
const addItem = (text: string) => {
  setItems((prev) => [...prev, text]);
};

オブジェクトも同じ原理で、user.name = 'new' ではなく setUser({ ...user, name: 'new' }) とする必要があります。Rork のプロンプトで「イミュータブルに更新して」と明示すると、この落とし穴を避けやすくなります。

よくある原因2: 古いクロージャが state を捕まえている

useEffectsetTimeout の中で state を参照すると、登録した瞬間の値が閉じ込められてしまうことがあります。

// ❌ count は常に 0 のまま
useEffect(() => {
  const id = setInterval(() => {
    setCount(count + 1);
  }, 1000);
  return () => clearInterval(id);
}, []);

[] 依存なので setInterval が参照している count は初回レンダー時の値(0)で凍結され、毎秒 setCount(0 + 1) が呼ばれ続けます。結果として 1 から先に進まない、という挙動になります。

// ✅ 関数形式の setState で前の値を取り出す
useEffect(() => {
  const id = setInterval(() => {
    setCount((prev) => prev + 1);
  }, 1000);
  return () => clearInterval(id);
}, []);

関数形式(setCount((prev) => ...))を使うと、そのときの最新 state が引数として渡ってくるので、クロージャに左右されません。これは Rork で useEffect が無限ループする原因と直し方 と裏表の関係にある落とし穴です。

よくある原因3: Zustand の selector が参照を比較していない

Rork で Zustand を使った状態管理コードを生成すると、こんな書き方が出てくることがあります。

// ❌ 新しいオブジェクトを毎回返すので無限再描画、もしくは更新されない
const { user, setUser } = useUserStore((s) => ({
  user: s.user,
  setUser: s.setUser,
}));

selector が毎回新しいオブジェクトを返すため、Zustand のデフォルトの浅い等価比較では「変化あり」と見なされて再描画が走ったり、逆に別の原因で参照が不安定になり更新に気づけなかったりします。

// ✅ プリミティブ値・関数を個別に取り出す
const user = useUserStore((s) => s.user);
const setUser = useUserStore((s) => s.setUser);

もしくは shallow 比較を明示的に指定してください。rork-zustand-shallow-comparison のようなキーワードでプロンプトに追記すると、Rork の生成コードもこちらに寄せられます。詳しい設計パターンは Rorkアプリの状態管理パターン完全ガイド に整理していますので、合わせてご覧いただくと理解が深まります。

よくある原因4: React.memo や useMemo の依存不足

パフォーマンス最適化のつもりで React.memouseMemo を入れると、依存配列の書き忘れで「古い値のまま固まる」事故が起きます。

// ❌ filter が変わっても再計算されない
const filteredList = useMemo(
  () => items.filter((i) => i.active),
  []   // ← items も filter も依存に入っていない
);

ESLint の react-hooks/exhaustive-deps ルールを有効にしていれば警告が出ますので、Rork でプロジェクトを立ち上げた直後にまずこのルールを ON にしておくことをおすすめします。

// ✅ 依存を正しく指定
const filteredList = useMemo(
  () => items.filter((i) => i.active),
  [items]
);

ついでに、React.memo(Component) でラップしたコンポーネントに毎回新しい関数やオブジェクトを props で渡していると、せっかくのメモ化が効かず再描画されます。props 側も useCallback / useMemo で安定化させるとセットで効果が出ます。

よくある原因5: 非同期処理の後に unmount 済みコンポーネントで setState

画面遷移直後に fetch のレスポンスが返ってきた場合、すでに unmount されているコンポーネントに setState しても、当然画面には反映されません。警告ログ(Can't perform a React state update on an unmounted component)が出ているなら、このパターンを疑ってください。

useEffect(() => {
  let alive = true;
  (async () => {
    const data = await fetch('/api/items').then((r) => r.json());
    if (!alive) return;             // unmount 後は setState しない
    setItems(data);
  })();
  return () => { alive = false; };
}, []);

もしくは AbortController を使って fetch 自体をキャンセルする方法もあります。リスト表示まわりの似たトラブルについては FlatList が空白で何も映らない—Rork アプリのリスト崩れを切り分ける もあわせて確認いただくと、表示が出ない系の切り分けがスムーズになります。

3 分で切り分ける診断フロー

原因を当てずっぽうで探すと沼るので、次の順に確認するのがおすすめです。

  • 1. 値が本当に変わっているか: 更新前後に console.log(prev, next) を挟み、参照まで変わっているか確認する(prev === next が true なら原因1)
  • 2. クロージャが古くないか: useEffectsetTimeout・イベントハンドラ内で state を直接参照していないか見直し、関数形式 setState に置き換えてみる
  • 3. 再描画は走っているか: コンポーネントの先頭で console.log('render', props) を入れて、そもそも再描画が呼ばれているか確認する(呼ばれていないなら memo・selector の問題)
  • 4. 親の state か子の state か: 子で値を変えているつもりで親の state を書き換えていないか、逆もないかを見る
  • 5. プラットフォーム依存か: iOS Simulator だけ・Android だけなど再現条件があるなら rork-ios-android-behavior-difference-fix の切り分けも検討する

再発を防ぐ小さな習慣

「直ったのにまた同じパターンでハマる」を繰り返さないために、私は次の 3 つを習慣にしています。

まず、ESLint の react-hooks/exhaustive-deps を必ず ON にします。Rork が生成したコードには警告が残っていることがあるので、プロジェクト開始時にまとめて潰しておくと後が楽です。

次に、state を更新する関数には必ず関数形式(setX((prev) => ...))を使うルールを自分に課しています。直近の値に依存しない場合も、形を揃えておくとクロージャ起因の事故がぐっと減ります。

最後に、Zustand や Context を使うときは「何を selector で取り出しているか」を必ずプリミティブ単位で見直します。オブジェクトを丸ごと取り出す書き方は、パフォーマンスと挙動の両方で不安定になりがちです。

まずは今ハマっているコードを上の「3 分診断フロー」の順で1つずつチェックしてみてください。大抵は原因1か原因2のどちらかで止まります。切り分けに必要な時間を 1 日から 10 分に短縮できれば、それだけで開発の体験は大きく変わります。

シェア

お読みいただきありがとうございます

Rork Lab は広告なしで運営しており、サーバー費用などの運営コストはメンバーシップのご支援で賄っています。実装コード・ベンチマーク・本番設計パターンなど、実務でお役立ていただける記事を毎日更新しています。もし読んでよかったと感じていただけましたら、ぜひご覧ください。

  • コピー&ペーストで使える実装コード付き
  • 毎日新しい上級ガイドを追加
  • ¥580/月 または ¥1,480 の永久アクセス
メンバーシップを見る →

もしこの記事がお役に立ちましたら、チップ(¥150)で応援いただけると大変励みになります。広告なしでの運営を続けるため、皆さまのご支援が大きな力になっています。

関連記事

開発ツール2026-07-14
AI再生成に壊されない設計 — Rork の生成コードと手書きロジックを分ける境界の引き方
Rork へ追加指示を出すたびに手書きのロジックが消える。プロンプトで守るのは対症療法です。生成コードと自分の所有コードを層で分け、再生成が届かない境界を設計で引く方法を、Zustand と service 層の実装例つきでお伝えします。
開発ツール2026-06-12
Rork 製アプリの『書類とデータ』が数 GB に膨らむときの原因と対処 — expo-image のディスクキャッシュ管理
アプリ本体は小さいのに『書類とデータ』だけが数 GB に膨らむ——壁紙アプリの運用で実際に踏んだ expo-image のディスクキャッシュ肥大を、cachePolicy の使い分け・縮小版配信・世代別クリアの三段構えで解決した記録です。
開発ツール2026-05-25
Rork で targetSdkVersion 35 にしたら Android のレイアウトが下まで埋まる問題の直し方
Android 15 (API 35) で強制されるエッジ・トゥ・エッジ表示によって、Rork で生成したアプリの下部タブやヘッダーがシステムバーに食い込む問題を、react-native-edge-to-edge と useSafeAreaInsets を使って実装パターン別に解決します。
📚RECOMMENDED BOOKS
大規模言語モデル入門
山田育矢
LLM開発
生成AIプロンプトエンジニアリング入門
我妻幸長
プロンプト
Claude CodeによるAI駆動開発入門
平川知秀
AI駆動開発
※ アフィリエイトリンクを含みます
もっと見る →