如果我想检查用户是否登录,我会收到错误消息:无法从其他组件的函数主体内部更新组件。
我有组件:
App.js
import {View} from 'native-base';
import React, {Component} from 'react';
import {StyleSheet, SafeAreaView} from 'react-native';
import {Provider} from 'react-redux';
import {createStore, applyMiddleware} from 'redux';
import ReduxThunk from 'redux-thunk';
import reducers from './reducers';
import StartUpRouter from './routers/StartUpRouter';
class App extends Component {
render() {
const store = createStore(reducers, {}, applyMiddleware(ReduxThunk));
return (
<SafeAreaView style={styles.container}>
<Provider store={store}>
<StartUpRouter />
</Provider>
</SafeAreaView>
);
}
}
const styles = StyleSheet.create({
container: {
flex: 1,
},
});
export default App;
-----------------------------------路由器文件夹---------- --------------------------
StartUpRouter.js
import React, {Component, useEffect} from 'react';
import {View, Text, ActivityIndicator, StyleSheet} from 'react-native';
import {NavigationContainer} from '@react-navigation/native';
import {createStackNavigator} from '@react-navigation/stack';
import * as RootNavigation from '../RootNavigation';
import {loginUser} from '../actions';
import {connect} from 'react-redux';
import LoginRouter from './LoginRouter';
const Stack = createStackNavigator();
function HomeScreen() {
return (
<View style={{flex: 1, alignItems: 'center', justifyContent: 'center'}}>
<Text>Home Screen</Text>
</View>
);
}
class StartUpRouter extends Component {
componentDidMount() {
this.props.loginUser();
}
render() {
const {auth} = this.props;
if (auth.loading) {
return (
<View style={styles.loading}>
<ActivityIndicator />
</View>
);
} else {
if (auth.isUserLogin) {
return (
<NavigationContainer ref={RootNavigation.navigationRef}>
<Stack.Navigator
screenOptions={{
headerTitleStyle: {
fontWeight: 'bold',
},
}}
initialRouteName="HomeScreen">
<Stack.Screen
name="HomeScreen"
component={HomeScreen}
options={{title: 'Please login'}}
/>
</Stack.Navigator>
</NavigationContainer>
);
} else {
return <LoginRouter {...this.props} />;
}
}
}
}
const styles = StyleSheet.create({
loading: {
flex: 1,
justifyContent: 'center',
},
});
const mapStateToProps = (state) => {
const {auth} = state;
return {auth};
};
export default connect(mapStateToProps, {loginUser})(StartUpRouter);
LoginRouter.js
import React, {Component} from 'react';
import {Text, View, StyleSheet} from 'react-native';
import {NavigationContainer} from '@react-navigation/native';
import {createStackNavigator} from '@react-navigation/stack';
import * as RootNavigation from '../RootNavigation';
import DefaultLogin from '../components/LoginScreens/DefaultLogin';
const Stack = createStackNavigator();
class LoginRouter extends Component {
render() {
return (
<NavigationContainer ref={RootNavigation.navigationRef}>
<Stack.Navigator initialRouteName="DefaultLogin">
<Stack.Screen
name="DefaultLogin"
component={DefaultLogin}
options={{title: 'Please login'}}
/>
</Stack.Navigator>
</NavigationContainer>
);
}
}
const styles = StyleSheet.create({});
export default LoginRouter;
----------------------------------- / Routers文件夹--------- ---------------------------
----------------------------------- Reducers文件夹---------- --------------------------
AuthReducer.js
import * as types from '../actions/types';
const INITIAL_STATE = {
loginUser: null,
isUserLogin: null,
loading: false,
};
export default (state = INITIAL_STATE, action) => {
switch (action.type) {
case types.LOGIN_USER:
return {...state, loading: true};
case types.LOGIN_USER_SUCCESS:
return {
...state,
...INITIAL_STATE,
isUserLogin: true,
loginUser: action.payload,
};
case types.LOGIN_USER_FAIL:
return {
...state,
loading: false,
isUserLogin: false,
};
default:
return state;
}
};
index.js
import {combineReducers} from 'redux';
import AuthReducer from './AuthReducer';
export default combineReducers({
auth: AuthReducer,
});
----------------------------------- / Reducers文件夹--------- ---------------------------
-----------------------------------动作文件夹---------- --------------------------
AuthActions.js
import * as types from './types';
import AsyncStorage from '@react-native-community/async-storage';
import * as RootNavigation from '../RootNavigation';
export const loginUser = () => async (dispatch) => {
dispatch({type: types.LOGIN_USER});
const loginUser = await AsyncStorage.getItem('loginUser');
if (loginUser) {
loginUserSuccess(dispatch, loginUser);
} else {
loginUserFail(dispatch);
}
};
const loginUserFail = (dispatch) => {
dispatch({type: types.LOGIN_USER_FAIL});
};
const loginUserSuccess = (dispatch, loginUser) => {
dispatch({
type: types.LOGIN_USER_SUCCESS,
payload: loginUser,
});
};
index.js
export * from './AuthActions';
types.js
export const LOGIN_USER = 'login_user';
export const LOGIN_USER_SUCCESS = 'login_user_success';
export const LOGIN_USER_FAIL = 'login_user_fail';
----------------------------------- / Actions文件夹--------- ---------------------------
-------------------------- componenst文件夹/普通文件夹---------------- ------------
index.js
export * from './SimpleHeader';
SimpleHeader.js
import React, {Component} from 'react';
import {Text, View, StyleSheet} from 'react-native';
class SimpleHeader extends Component {
render() {
const {headerStyle, headerTextStyle} = styles;
const {headerText} = this.props;
return (
<View style={headerStyle}>
<View>
<Text style={headerTextStyle}>{headerText}</Text>
</View>
</View>
);
}
}
const styles = StyleSheet.create({
headerStyle: {
alignItems: 'center',
flexDirection: 'row',
justifyContent: 'flex-start',
},
headerTextStyle: {
fontSize: 25,
paddingLeft: 25,
},
});
export {SimpleHeader};
-------------------------- / componenst_folder / common_folder ----------------- -----------
-------------------------- componenst_folder / LoginScreens_folder ------------------ ----------
DefaultLogin.js
import React, {Component} from 'react';
import {Text, View, StyleSheet, TouchableOpacity} from 'react-native';
import {SimpleHeader} from '../common';
import LinearGradient from 'react-native-linear-gradient';
import MaskedView from '@react-native-community/masked-view';
import Config from 'react-native-config';
import {Button} from 'native-base';
class DefaultLogin extends Component {
render() {
const {navigation} = this.props;
{* if I comment navigation.setOption() function the error disappear *}
navigation.setOptions({
headerTitle: (props) => <SimpleHeader headerText="Login" />,
});
const {
container,
maskViewContainer,
maskViewObj,
linearGradientObj,
qrButtonContainer,
qrCodeText,
button,
buttonTextStyle,
footerStyle,
footerTextStyle,
} = styles;
return (
<View style={container}>
<View style={maskViewContainer}>
<MaskedView
style={maskViewObj}
maskElement={
<View>
</View>
}>
<LinearGradient
colors={['#00A8B0', '#78BE20']}
style={linearGradientObj}
start={{x: 0, y: 0}}
end={{x: 1, y: 0}}
/>
</MaskedView>
</View>
<View style={qrButtonContainer}>
<Text style={qrCodeText}>
For login please scan QR CODE
</Text>
<Button
block
onPress={() =>
this.props.navigation.navigate('barcodeLogin')
}
style={button}>
<Text style={buttonTextStyle}>Login by QR CODE</Text>
</Button>
</View>
<View style={footerStyle}>
<Text style={footerTextStyle}>
Use username and password
</Text>
</View>
</View>
);
}
}
const styles = StyleSheet.create({
container: {
flex: 1,
marginTop: 50,
},
maskViewObj: {
flex: 1,
flexDirection: 'row',
},
linearGradientObj: {
flex: 1,
},
maskViewContainer: {
alignItems: 'center',
height: 250,
},
qrButtonContainer: {
height: 100,
flex: 1,
width: null,
marginLeft: 10,
marginRight: 10,
marginTop: 60,
alignItems: 'center',
},
qrCodeText: {
color: '#525F6B',
fontSize: 14,
marginBottom: 10,
},
button: {
backgroundColor: '#005691',
borderRadius: 0,
},
buttonTextStyle: {
color: '#FFFFFF',
fontSize: 22,
marginLeft: 30,
marginRight: 30,
},
footerStyle: {
height: 30,
flex: 1,
width: null,
justifyContent: 'flex-end',
alignItems: 'center',
marginBottom: 30,
},
footerTextStyle: {
color: '#005691',
},
});
export default DefaultLogin;
-------------------------- / componenst_folder / LoginScreens_folder ----------------- -----------
基本上这是Entyre应用程序。
如果我评论功能
navigation.setOptions({
headerTitle: (props) => <SimpleHeader headerText="Login" />,
});
错误:无法从其他组件的功能主体内部更新组件消失
从控制台登录:
Warning: Cannot update a component from inside the function body of a different component.
in DefaultLogin (at SceneView.tsx:122)
in StaticContainer
in StaticContainer (at SceneView.tsx:115)
in EnsureSingleNavigator (at SceneView.tsx:114)
in SceneView (at useDescriptors.tsx:150)
in RCTView (at View.js:34)
in View (at CardContainer.tsx:221)
in RCTView (at View.js:34)
in View (at CardContainer.tsx:220)
in RCTView (at View.js:34)
in View (at CardSheet.tsx:33)
in ForwardRef(CardSheet) (at Card.tsx:563)
in RCTView (at View.js:34)
in View (at createAnimatedComponent.js:165)
in AnimatedComponent (at createAnimatedComponent.js:215)
in ForwardRef(AnimatedComponentWrapper) (at Card.tsx:545)
in PanGestureHandler (at GestureHandlerNative.tsx:13)
in PanGestureHandler (at Card.tsx:539)
in RCTView (at View.js:34)
in View (at createAnimatedComponent.js:165)
in AnimatedComponent (at createAnimatedComponent.js:215)
in ForwardRef(AnimatedComponentWrapper) (at Card.tsx:535)
in RCTView (at View.js:34)
in View (at Card.tsx:529)
in Card (at CardContainer.tsx:189)
in CardContainer (at CardStack.tsx:558)
in RCTView (at View.js:34)
in View (at Screens.tsx:69)
in MaybeScreen (at CardStack.tsx:551)
in RCTView (at View.js:34)
in View (at Screens.tsx:48)
in MaybeScreenContainer (at CardStack.tsx:461)
in CardStack (at StackView.tsx:458)
in KeyboardManager (at StackView.tsx:456)
in RNCSafeAreaProvider (at SafeAreaContext.tsx:74)
in SafeAreaProvider (at SafeAreaProviderCompat.tsx:42)
in SafeAreaProviderCompat (at StackView.tsx:453)
in RCTView (at View.js:34)
in View (at StackView.tsx:452)
in StackView (at createStackNavigator.tsx:84)
in StackNavigator (at LoginRouter.js:14)
in EnsureSingleNavigator (at BaseNavigationContainer.tsx:390)
in ForwardRef(BaseNavigationContainer) (at NavigationContainer.tsx:91)
in ThemeProvider (at NavigationContainer.tsx:90)
in ForwardRef(NavigationContainer) (at LoginRouter.js:13)
in LoginRouter (at StartUpRouter.js:61)
in StartUpRouter (created by ConnectFunction)
in ConnectFunction (at App.js:16)
in Provider (at App.js:15)
in RCTSafeAreaView (at SafeAreaView.js:51)
in ForwardRef(SafeAreaView) (at App.js:14)
in App (at renderApplication.js:45)
in RCTView (at View.js:34)
in View (at AppContainer.js:106)
in RCTView (at View.js:34)
in View (at AppContainer.js:132)
in AppContainer (at renderApplication.js:39)
有人可以帮忙吗?
谢谢
答案 0 :(得分:1)
componentDid 对我有用。 使用无状态组件:
const DefaultLogin = ({ navigation }) => {
useEffect(() => {
navigation.setOptions({
headerTitle: () => <SimpleHeader headerText="Choose login" />,
headerBackground: () => <BoschBottomStrip />,
});
}, [navigation]);
return (
// ...
)
}
答案 1 :(得分:0)
我找到了解决方案,但不满意,因为我无法从组件DefaultLogin.js中更新/设置组件SimpleHeader.js,而只能从父组件LoginRouter.js中更新/设置
我更改了:
DefaultLogin.js(已删除功能Navigation.setOption({....}))
import React, {Component} from 'react';
import {Text, View, StyleSheet, TouchableOpacity} from 'react-native';
import {SimpleHeader} from '../common';
import LinearGradient from 'react-native-linear-gradient';
import MaskedView from '@react-native-community/masked-view';
import Config from 'react-native-config';
import {Button} from 'native-base';
class DefaultLogin extends Component {
render() {
const {navigation} = this.props;
{* if I comment navigation.setOption() function the error disappear *}
navigation.setOptions({
headerTitle: (props) => <SimpleHeader headerText="Login" />,
});
const {
container,
maskViewContainer,
maskViewObj,
linearGradientObj,
qrButtonContainer,
qrCodeText,
button,
buttonTextStyle,
footerStyle,
footerTextStyle,
} = styles;
return (
<View style={container}>
<View style={maskViewContainer}>
<MaskedView
style={maskViewObj}
maskElement={
<View>
</View>
}>
<LinearGradient
colors={['#00A8B0', '#78BE20']}
style={linearGradientObj}
start={{x: 0, y: 0}}
end={{x: 1, y: 0}}
/>
</MaskedView>
</View>
<View style={qrButtonContainer}>
<Text style={qrCodeText}>
For login please scan QR CODE
</Text>
<Button
block
onPress={() =>
this.props.navigation.navigate('barcodeLogin')
}
style={button}>
<Text style={buttonTextStyle}>Login by QR CODE</Text>
</Button>
</View>
<View style={footerStyle}>
<Text style={footerTextStyle}>
Use username and password
</Text>
</View>
</View>
);
}
}
.
.
.
收件人:
class DefaultLogin extends Component {
render() {
const {
container,
maskViewContainer,
maskViewObj,
linearGradientObj,
qrButtonContainer,
qrCodeText,
button,
buttonTextStyle,
footerStyle,
footerTextStyle,
} = styles;
return (
<View style={container}>
<View style={maskViewContainer}>
<MaskedView
style={maskViewObj}
maskElement={
<View>
</View>
}>
<LinearGradient
colors={['#00A8B0', '#78BE20']}
style={linearGradientObj}
start={{x: 0, y: 0}}
end={{x: 1, y: 0}}
/>
</MaskedView>
</View>
<View style={qrButtonContainer}>
<Text style={qrCodeText}>
For login scan QR CODE
</Text>
<Button
block
onPress={() =>
this.props.navigation.navigate('barcodeLogin')
}
style={button}>
<Text style={buttonTextStyle}>Login by QR CODE</Text>
</Button>
</View>
<View style={footerStyle}>
<Text style={footerTextStyle}>
Use username and password
</Text>
</View>
</View>
);
}
}
.
.
.
然后我从父组件LoginRouter.js设置标头
import React, {Component} from 'react';
import {Text, View, StyleSheet} from 'react-native';
import {NavigationContainer} from '@react-navigation/native';
import {createStackNavigator} from '@react-navigation/stack';
import * as RootNavigation from '../RootNavigation';
import DefaultLogin from '../components/LoginScreens/DefaultLogin';
import {SimpleHeader} from '../components/common';
const Stack = createStackNavigator();
class LoginRouter extends Component {
render() {
return (
<NavigationContainer ref={RootNavigation.navigationRef}>
<Stack.Navigator initialRouteName="DefaultLogin">
<Stack.Screen
name="DefaultLogin"
component={DefaultLogin}
options={{
headerTitle: (props) => (
<SimpleHeader headerText="Login" />
),
}}
/>
</Stack.Navigator>
</NavigationContainer>
);
}
}
const styles = StyleSheet.create({});
export default LoginRouter;
为什么我不能在组件DefaultLogin.js内使用功能Navigation.setOptions({...})来设置标头,而只能从父组件LoginRouter.js来设置标头?
感谢大家的回答
答案 2 :(得分:0)
最后我找到了解决方法:)这很简单:)
我通过componentDidUpdate(){}包装了navigation.setOption({...})
DefaultLogin.js
class DefaultLogin extends Component {
componentDidMount() {
const {navigation} = this.props;
navigation.setOptions({
headerTitle: () => <SimpleHeader headerText="Choose login" />,
headerBackground: () => <BoschBottomStrip />,
});
}
render() {
const {
container,
maskViewContainer,
.
.
.
.
现在我可以从使用SimpleHeader的组件中设置标头。
答案 3 :(得分:0)
另一种解决方案是不在子屏幕内调用navigation.setOptions
,而是将其移动到父组件屏幕的options
中。
所以以前我们有:
父母:
<NavigationContainer>
<Stack.Navigator>
<Stack.Screen
name="Root"
component={BottomTabNavigator}
/>
</Stack.Navigator>
</NavigationContainer>
和孩子(BottomTabNavigator.jsx):
const BottomTabNavigator = ({ navigation }) => {
navigation.setOptions({
headerShown: false,
});
}
现在,我们可以删除子项中的setOptions
调用,并将其放入父项中:
<NavigationContainer>
<Stack.Navigator>
<Stack.Screen
name="Root"
component={BottomTabNavigator}
options={{
headerShown: false,
}}
/>
</Stack.Navigator>
</NavigationContainer>