質問をすることでしか得られない、回答やアドバイスがある。

15分調べてもわからないことは、質問しよう!

新規登録して質問してみよう
ただいま回答率
85.35%
React Native

React Nativeは、ネイティブモバイルアプリ(iOS/Android)を作成できるJavaScriptフレームワークです。Reactと同じ設計のため、宣言的なコンポーネントでリッチなUIを開発することが可能です。

React.js

Reactは、アプリケーションのインターフェースを構築するためのオープンソースJavaScriptライブラリです。

Q&A

0回答

862閲覧

【React Navigation】useEffect内で定義したヘッダーオプションを更新できるようにしたい

moootoko_ojisan

総合スコア32

React Native

React Nativeは、ネイティブモバイルアプリ(iOS/Android)を作成できるJavaScriptフレームワークです。Reactと同じ設計のため、宣言的なコンポーネントでリッチなUIを開発することが可能です。

React.js

Reactは、アプリケーションのインターフェースを構築するためのオープンソースJavaScriptライブラリです。

0グッド

0クリップ

投稿2022/01/10 08:56

編集2022/01/10 09:05

前提・実現したいこと

Cloud Firestoreと連携してリストを表示するアプリを作っています。

現在、ReactNavigationのStackNavigatorを使ってヘッダーを作っているところです。
ヘッダー右部にスイッチとアイコンを設置する予定で、機能は

 スイッチ:リストのモードを切り替える(仮にAモード・Bモードとします)
アイコン:アプリのログアウト(Firestoreのリアルタイム連携を停止)

です。
画面の概形は以下のような感じです。青部分がリストで、モードによって表示内容が変わります。

イメージ説明

発生している問題・エラーメッセージ

スイッチをタップしたとき、リストは切り替わるがスイッチのON/OFFが正常に切り替わりません。

例えば、Aモード(OFF)からBモード(ON)に切り替えるとき

  • スイッチをタップする
  • スイッチがONになる
  • 少し時間をおいて、リストがBモードになる
  • スイッチがOFFに戻る(リストはBモードを維持)

という挙動です。

該当のソースコード

javascript

1import React, { useEffect, useState } from 'react'; 2import { View, StyleSheet, Text, Alert } from 'react-native'; 3import { MaterialCommunityIcons } from '@expo/vector-icons'; 4import { Switch } from 'react-native-paper'; 5import { getAuth, onAuthStateChanged } from '@firebase/auth'; 6import { 7 getFirestore, 8 collection, 9 onSnapshot, 10 where, 11 orderBy, 12 limit, 13 query, 14} from 'firebase/firestore'; 15 16// ローディング中を表示するコンポーネント 17import Loading from '../components/Loading'; 18// 画面概形の青部分に当たるリスト 19import ShareList from '../components/ShareList'; 20// アプリからログアウトするボタン 21import LogoutButton from '../components/LogoutButton'; 22 23 24export default function ShareListScreen({ navigation }) { 25 // Aモードのリスト(list:リストデータ, loading:ローディン中ならtrue) 26 const [shareListState, setShareListState] = useState({ 27 list: [], 28 loading: false, 29 }); 30 // Bモードのリスト(list:リストデータ, loading:ローディン中ならtrue) 31 const [okawariListState, setOkawariListState] = useState({ 32 list: [], 33 loading: false, 34 }); 35 // Aモードならfalse, Bモードならtrue 36 const [listModeSwitched, setListModeSwitched] = useState(true); 37 38 // スイッチをタップした時の処理 39 const toggleSwitch = () => { 40 setListModeSwitched((prev) => !prev); 41 }; 42 43 useEffect(() => { 44 // 各モードのリストをローディング中に設定 45 setShareListState((prev) => ({ ...prev, loading: true })); 46 setOkawariListState((prev) => ({ ...prev, loading: true })); 47 48 // クリーンアップ関数 49 const cleanupFuncs = { 50 auth: () => {}, 51 shareList: () => {}, 52 okawariList: () => {}, 53 }; 54 55 cleanupFuncs.auth = onAuthStateChanged(getAuth(), (user) => { 56 if (user) { 57 // Aモードのリストのスナップショット取得(取得完了時にローディングをfalseに設定) 58 cleanupFuncs.shareList = onSnapshot( ... ); 59 // Bモードのリストのスナップショット取得(取得完了時にローディングをfalseに設定) 60 cleanupFuncs.okawariList = onSnapshot( ... ); 61 } 62 63 // ヘッダーオプション 64 navigation.setOptions({ 65 headerRight: () => ( 66 <View style={styles.headerRightContainer}> 67 <View style={styles.listModeSwitchFrame}> 68 <MaterialCommunityIcons 69 name='rice' 70 size={24} 71 color='#000000' 72 style={styles.listModeSwitchIcon} 73 /> 74 <Switch value={listModeSwitched} onValueChange={toggleSwitch} /> 75 </View> 76 <View style={styles.lougoutButtonFrame}> 77 {/* ログアウト時にクリーンアップ関数を実行させるため、propsで渡している */} 78 <LogoutButton cleanupFuncs={cleanupFuncs} /> 79 </View> 80 </View> 81 ), 82 }); 83 }); 84 85 return () => { 86 cleanupFuncs.auth(); 87 cleanupFuncs.shareList(); 88 cleanupFuncs.okawariList(); 89 }; 90 }, []); 91 92 if (listModeSwitched) { 93 // Bモードで表示 94 } else { 95 // Aモードで表示 96 } 97} 98 99const styles = StyleSheet.create({ ... }); 100

考えられる原因

useEffect内でヘッダーオプションを設定しているのが原因だと考えています。
スイッチがもとに戻ってしまう現象は、DOMマウント後1度だけ実行されるuseEffect時のスイッチの状態(
<Switch value={false} onValueChange={toggleSwitch} />)が保持されているためと予想しています。

試したこと

・クリーンアップ関数をuseStateで保持して、toggleSwitch関数内でヘッダーオプションを再設定
⇒改善されず

・useEffectの実行をlistModeSwitched依存に変更
⇒スイッチを切り替えるたびにスナップショット取得が実行されて、動作が非常に遅くなってしまう

質問したいこと

①ヘッダーオプションをuseEffectで定義する状態のまま、スイッチの動作が想定通りになる方法があればお聞きしたいです。

headerRightを一度で定義せずあとからコンポーネントを追加できるような方法はあるのでしょうか。
(useEffectでログアウトボタン追加、他の部分でスイッチ追加 のように)

補足情報(FW/ツールのバージョンなど)

react : 17.0.1
react-native : 0.64.3
@react-navigation/native : 6.0.6
@react-navigation/stack : 6.0.11
react-native-paper : 4.11.1

気になる質問をクリップする

クリップした質問は、後からいつでもMYページで確認できます。

またクリップした質問に回答があった際、通知やメールを受け取ることができます。

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

guest

あなたの回答

tips

太字

斜体

打ち消し線

見出し

引用テキストの挿入

コードの挿入

リンクの挿入

リストの挿入

番号リストの挿入

表の挿入

水平線の挿入

プレビュー

まだ回答がついていません

会員登録して回答してみよう

アカウントをお持ちの方は

15分調べてもわからないことは
teratailで質問しよう!

ただいまの回答率
85.35%

質問をまとめることで
思考を整理して素早く解決

テンプレート機能で
簡単に質問をまとめる

質問する

関連した質問