React Native - 警告:无法从不同组件的函数体内部更新组件
React Native - Warning: Cannot update a component from inside the function body of a different component
我知道这里还有其他答案,但我找不到适合我的应用程序结构的答案。我使用他们的文档使用 Firebase 身份验证设置了我的应用程序,但是一旦它显示了如何创建持久登录,它就没有显示如何注销和重新呈现主屏幕。
这是我无法解决的警告:
警告:无法从不同组件的函数体内更新组件。
在 [native code]:null in dispatchAction
在 node_modules/@react-navigation/core/src/useNavigationCache.tsx:100:22 在 acc.route.key.setOptions
在 node_modules/@firebase/webchannel-wrapper/dist/index.js:42:151 在 Rb$argument_0
在 [本机代码]:null 在 performSyncWorkOnRoot
在 [native code]:null in forEach
在 node_modules/react-native/Libraries/Core/setUpReactRefresh.js:43:6 在 Refresh.performReactRefresh
在 [本地代码]:callFunctionReturnFlushedQueue
中为 null
这是我的 App.js 文件:
import React, { useState, useEffect } from 'react';
import { createStackNavigator } from '@react-navigation/stack';
import { NavigationContainer } from '@react-navigation/native';
import { Login, Home, Events, CreateEvent, EventDetails } from './src/screens/';
import { Registration } from './src/navigation';
import { firebase } from './src/firebase/config';
import { TouchableOpacity, Image, View } from 'react-native';
import Logout from './src/screens/Logout/Logout';
const Stack = createStackNavigator();
export default function App() {
const [loading, setLoading] = useState(true)
const [user, setUser] = useState(null);
useEffect(() => {
const usersRef = firebase.firestore().collection('users');
firebase.auth().onAuthStateChanged(user => {
if (user) {
usersRef
.doc(user.uid)
.get()
.then((document) => {
const userData = document.data()
setLoading(false)
setUser(userData);
})
.catch((error) => {
setLoading(false)
});
} else {
setLoading(false)
}
});
}, [user]);
if (loading) {
return (
<></>
)
}
return (
<NavigationContainer>
<Stack.Navigator>
<Stack.Screen
name="Home"
options={{
headerTitle: 'Showday',
headerTitleAlign: 'center',
headerRight: () => (
<TouchableOpacity
onPress={() => alert('This is the menu button')}>
<Image source={require('./assets/menuicon.png')} />
</TouchableOpacity>
),
headerLeftContainerStyle: {marginLeft: 20},
headerRightContainerStyle: {marginRight: 20}}}>
{props => <Home {...props} extraData={user} />}
</Stack.Screen>
<Stack.Screen
name="Events"
options={{
headerTitle: 'Events',
headerTitleAlign: 'center',
headerRight: () => (
<TouchableOpacity
onPress={() => alert('This is the menu button')}
style={{ marginLeft: 20}} >
<Image source={require('./assets/menuicon.png')} />
</TouchableOpacity>
),
headerRightContainerStyle: {marginRight: 20}}}>
{props => <Events {...props} extraData={user} />}
</Stack.Screen>
<Stack.Screen
name='CreateEvent'
options={{
headerTitle: 'Create New Event',
headerTitleAlign: 'center',
headerRight: () => (
<TouchableOpacity
onPress={() => alert('This is the menu button')}
style={{ marginLeft: 20}} >
<Image source={require('./assets/menuicon.png')} />
</TouchableOpacity>
),
headerRightContainerStyle: {marginRight: 20}}}>
{props => <CreateEvent {...props} extraData={user} />}
</Stack.Screen>
<Stack.Screen name='EventDetails' options={{headerTitle: 'Event Details', headerTitleAlign: 'center'}}>
{props => <EventDetails {...props} />}
</Stack.Screen>
<Stack.Screen name='Login' component={Login}/>
<Stack.Screen name='Logout' component={Logout} />
<Stack.Screen name='Register' component={Registration} />
</Stack.Navigator>
</NavigationContainer>
);
}
这是我的 Home.js 文件,它根据用户是否登录显示登录或注销:
import React, { useState } from 'react';
import { useNavigation } from '@react-navigation/native';
import { Text, View, TouchableOpacity, Image } from 'react-native';
import { firebase } from '../../firebase/config';
import styles from './HomeStyles';
function Home(props) {
const [loginButtonDisplay, setLoginButtonDisplay] = useState(true)
const user = props.extraData;
const nav = useNavigation();
const login = () => {
nav.navigate('Login');
}
const logout = () => {
nav.navigate('Logout');
}
const renderLoginLogout = () => {
if (user) {
setLoginButtonDisplay(false);
}
if (loginButtonDisplay) {
return (
<TouchableOpacity
onPress={login}>
<Text>Login</Text>
{/* <Image source={require('../../../assets/login.png')} /> */}
</TouchableOpacity>)
} else {
return (
<TouchableOpacity
onPress={logout}>
<Text>Logout</Text>
{/* <Image source={require('../../../assets/logout.png')} /> */}
</TouchableOpacity>
);
}
}
nav.setOptions({
headerLeft: () => (
<View>
{renderLoginLogout()}
</View>
),
})
const onEventsPressed = () => {
nav.navigate('Events');
}
return (
<View style={styles.container}>
<View style={styles.button__row}>
<TouchableOpacity
style={styles.button}
onPress={onEventsPressed}>
<Text style={styles.button__text}>{'Upcoming\nEvents'}</Text>
</TouchableOpacity>
<TouchableOpacity
style={styles.button}
onPress={() => null}>
<Text style={styles.button__text}>Shows Today</Text>
</TouchableOpacity>
</View>
<View style={styles.button__row}>
<TouchableOpacity
style={styles.button}
onPress={() => null}>
<Text style={styles.button__text}>Stats</Text>
</TouchableOpacity>
<TouchableOpacity
style={styles.button}
onPress={() => null}>
<Text style={styles.button__text}>Locations</Text>
</TouchableOpacity>
</View>
<View style={styles.button__row}>
<TouchableOpacity
style={styles.button}
onPress={() => null}>
<Text style={styles.button__text}>Workouts</Text>
</TouchableOpacity>
<TouchableOpacity
style={styles.button}
onPress={() => null}>
<Text style={styles.button__text}>More</Text>
</TouchableOpacity>
</View>
</View>
);
}
export default Home;
如果用户已登录,我会将他们带到注销屏幕要求确认,当我返回主屏幕时它仍然显示 'Logout' 而不是 'Login'
import React, { useState } from 'react';
import { Text, View, TouchableOpacity, TextInput, Keyboard } from 'react-native';
import { useNavigation } from '@react-navigation/native';
import Spinner from 'react-native-loading-spinner-overlay';
import { firebase } from '../../firebase/config';
import styles from './LogoutStyles';
function Logout () {
const [visible, setVisible] = useState(false);
const nav = useNavigation();
const yesPressed = () => {
setVisible(true);
firebase.auth().signOut().catch(error => alert(error.message));
nav.goBack();
}
const noPressed = () => nav.goBack();
return (
<View style={styles.container}>
<Spinner visible={visible} />
<Text style={styles.text}>Are you sure you want to log out?</Text>
<View style={styles.button__container}>
<TouchableOpacity
onPress={yesPressed}
style={styles.button}>
<Text style={styles.button__text}>Yes</Text>
</TouchableOpacity>
<TouchableOpacity
onPress={noPressed}
style={styles.button}>
<Text style={styles.button__text}>No</Text>
</TouchableOpacity>
</View>
</View>
);
}
export default Logout;
非常感谢任何对此警告的帮助。
您的代码在最常见的用例中没有问题,但导航选项是在第一次渲染时构建的,因此每次带有道具更改的新渲染都不会更新它们。
您需要在 useEffect 挂钩上管理 navigation.setOption,因此每次更改 useEffect 依赖项时,您都会更新导航选项:
React.useEffect(()=>{
nav.setOptions({
headerLeft: () => (
<View>
{renderLoginLogout()}
</View>
),
})
},[user])
我知道这里还有其他答案,但我找不到适合我的应用程序结构的答案。我使用他们的文档使用 Firebase 身份验证设置了我的应用程序,但是一旦它显示了如何创建持久登录,它就没有显示如何注销和重新呈现主屏幕。
这是我无法解决的警告:
警告:无法从不同组件的函数体内更新组件。 在 [native code]:null in dispatchAction 在 node_modules/@react-navigation/core/src/useNavigationCache.tsx:100:22 在 acc.route.key.setOptions 在 node_modules/@firebase/webchannel-wrapper/dist/index.js:42:151 在 Rb$argument_0 在 [本机代码]:null 在 performSyncWorkOnRoot 在 [native code]:null in forEach 在 node_modules/react-native/Libraries/Core/setUpReactRefresh.js:43:6 在 Refresh.performReactRefresh 在 [本地代码]:callFunctionReturnFlushedQueue
中为 null这是我的 App.js 文件:
import React, { useState, useEffect } from 'react';
import { createStackNavigator } from '@react-navigation/stack';
import { NavigationContainer } from '@react-navigation/native';
import { Login, Home, Events, CreateEvent, EventDetails } from './src/screens/';
import { Registration } from './src/navigation';
import { firebase } from './src/firebase/config';
import { TouchableOpacity, Image, View } from 'react-native';
import Logout from './src/screens/Logout/Logout';
const Stack = createStackNavigator();
export default function App() {
const [loading, setLoading] = useState(true)
const [user, setUser] = useState(null);
useEffect(() => {
const usersRef = firebase.firestore().collection('users');
firebase.auth().onAuthStateChanged(user => {
if (user) {
usersRef
.doc(user.uid)
.get()
.then((document) => {
const userData = document.data()
setLoading(false)
setUser(userData);
})
.catch((error) => {
setLoading(false)
});
} else {
setLoading(false)
}
});
}, [user]);
if (loading) {
return (
<></>
)
}
return (
<NavigationContainer>
<Stack.Navigator>
<Stack.Screen
name="Home"
options={{
headerTitle: 'Showday',
headerTitleAlign: 'center',
headerRight: () => (
<TouchableOpacity
onPress={() => alert('This is the menu button')}>
<Image source={require('./assets/menuicon.png')} />
</TouchableOpacity>
),
headerLeftContainerStyle: {marginLeft: 20},
headerRightContainerStyle: {marginRight: 20}}}>
{props => <Home {...props} extraData={user} />}
</Stack.Screen>
<Stack.Screen
name="Events"
options={{
headerTitle: 'Events',
headerTitleAlign: 'center',
headerRight: () => (
<TouchableOpacity
onPress={() => alert('This is the menu button')}
style={{ marginLeft: 20}} >
<Image source={require('./assets/menuicon.png')} />
</TouchableOpacity>
),
headerRightContainerStyle: {marginRight: 20}}}>
{props => <Events {...props} extraData={user} />}
</Stack.Screen>
<Stack.Screen
name='CreateEvent'
options={{
headerTitle: 'Create New Event',
headerTitleAlign: 'center',
headerRight: () => (
<TouchableOpacity
onPress={() => alert('This is the menu button')}
style={{ marginLeft: 20}} >
<Image source={require('./assets/menuicon.png')} />
</TouchableOpacity>
),
headerRightContainerStyle: {marginRight: 20}}}>
{props => <CreateEvent {...props} extraData={user} />}
</Stack.Screen>
<Stack.Screen name='EventDetails' options={{headerTitle: 'Event Details', headerTitleAlign: 'center'}}>
{props => <EventDetails {...props} />}
</Stack.Screen>
<Stack.Screen name='Login' component={Login}/>
<Stack.Screen name='Logout' component={Logout} />
<Stack.Screen name='Register' component={Registration} />
</Stack.Navigator>
</NavigationContainer>
);
}
这是我的 Home.js 文件,它根据用户是否登录显示登录或注销:
import React, { useState } from 'react';
import { useNavigation } from '@react-navigation/native';
import { Text, View, TouchableOpacity, Image } from 'react-native';
import { firebase } from '../../firebase/config';
import styles from './HomeStyles';
function Home(props) {
const [loginButtonDisplay, setLoginButtonDisplay] = useState(true)
const user = props.extraData;
const nav = useNavigation();
const login = () => {
nav.navigate('Login');
}
const logout = () => {
nav.navigate('Logout');
}
const renderLoginLogout = () => {
if (user) {
setLoginButtonDisplay(false);
}
if (loginButtonDisplay) {
return (
<TouchableOpacity
onPress={login}>
<Text>Login</Text>
{/* <Image source={require('../../../assets/login.png')} /> */}
</TouchableOpacity>)
} else {
return (
<TouchableOpacity
onPress={logout}>
<Text>Logout</Text>
{/* <Image source={require('../../../assets/logout.png')} /> */}
</TouchableOpacity>
);
}
}
nav.setOptions({
headerLeft: () => (
<View>
{renderLoginLogout()}
</View>
),
})
const onEventsPressed = () => {
nav.navigate('Events');
}
return (
<View style={styles.container}>
<View style={styles.button__row}>
<TouchableOpacity
style={styles.button}
onPress={onEventsPressed}>
<Text style={styles.button__text}>{'Upcoming\nEvents'}</Text>
</TouchableOpacity>
<TouchableOpacity
style={styles.button}
onPress={() => null}>
<Text style={styles.button__text}>Shows Today</Text>
</TouchableOpacity>
</View>
<View style={styles.button__row}>
<TouchableOpacity
style={styles.button}
onPress={() => null}>
<Text style={styles.button__text}>Stats</Text>
</TouchableOpacity>
<TouchableOpacity
style={styles.button}
onPress={() => null}>
<Text style={styles.button__text}>Locations</Text>
</TouchableOpacity>
</View>
<View style={styles.button__row}>
<TouchableOpacity
style={styles.button}
onPress={() => null}>
<Text style={styles.button__text}>Workouts</Text>
</TouchableOpacity>
<TouchableOpacity
style={styles.button}
onPress={() => null}>
<Text style={styles.button__text}>More</Text>
</TouchableOpacity>
</View>
</View>
);
}
export default Home;
如果用户已登录,我会将他们带到注销屏幕要求确认,当我返回主屏幕时它仍然显示 'Logout' 而不是 'Login'
import React, { useState } from 'react';
import { Text, View, TouchableOpacity, TextInput, Keyboard } from 'react-native';
import { useNavigation } from '@react-navigation/native';
import Spinner from 'react-native-loading-spinner-overlay';
import { firebase } from '../../firebase/config';
import styles from './LogoutStyles';
function Logout () {
const [visible, setVisible] = useState(false);
const nav = useNavigation();
const yesPressed = () => {
setVisible(true);
firebase.auth().signOut().catch(error => alert(error.message));
nav.goBack();
}
const noPressed = () => nav.goBack();
return (
<View style={styles.container}>
<Spinner visible={visible} />
<Text style={styles.text}>Are you sure you want to log out?</Text>
<View style={styles.button__container}>
<TouchableOpacity
onPress={yesPressed}
style={styles.button}>
<Text style={styles.button__text}>Yes</Text>
</TouchableOpacity>
<TouchableOpacity
onPress={noPressed}
style={styles.button}>
<Text style={styles.button__text}>No</Text>
</TouchableOpacity>
</View>
</View>
);
}
export default Logout;
非常感谢任何对此警告的帮助。
您的代码在最常见的用例中没有问题,但导航选项是在第一次渲染时构建的,因此每次带有道具更改的新渲染都不会更新它们。
您需要在 useEffect 挂钩上管理 navigation.setOption,因此每次更改 useEffect 依赖项时,您都会更新导航选项:
React.useEffect(()=>{
nav.setOptions({
headerLeft: () => (
<View>
{renderLoginLogout()}
</View>
),
})
},[user])