1

if I want to check if user is logged in I gets error: Cannot update a component from inside the function body of a different component.

I have components:

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;

----------------------------------- Routers folder ------------------------------------

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 folder ------------------------------------

----------------------------------- Reducers folder ------------------------------------

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 folder ------------------------------------

----------------------------------- Actions folder ------------------------------------

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 folder ------------------------------------

-------------------------- componenst folder/common folder ----------------------------

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 ----------------------------

Basically this is entyre application.

If I comment function

navigation.setOptions({
            headerTitle: (props) => <SimpleHeader headerText="Login" />,
        });

the error: Cannot update a component from inside the function body of a different component disappear

Log from console:

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)

Could somebody help please?

Thanks

Petrik-a-dost
  • 223
  • 4
  • 11
  • Does this help ? https://stackoverflow.com/questions/60526786/react-warning-cannot-update-a-component-from-inside-the-function-body-of-a-diff – WaLinke Sep 30 '20 at 11:38
  • const store = createStore(reducers, {}, applyMiddleware(ReduxThunk)); create this outside the class component – Ashwith Saldanha Sep 30 '20 at 12:06
  • if create this inside render everytime the app renders this will be created again and again – Ashwith Saldanha Sep 30 '20 at 12:07
  • @AshwithSaldanha I moved "const store = createStore(reducers, {}, applyMiddleware(ReduxThunk)); " outside of component but it didn't help :( – Petrik-a-dost Sep 30 '20 at 13:28

3 Answers3

3

Finally I found solution :) it is simple :)

I wrapped navigation.setOption({ ... }) by componentDidUpdate(){}

DefaultLogin.js

class DefaultLogin extends Component {
    componentDidMount() {
        const {navigation} = this.props;
        navigation.setOptions({
            headerTitle: () => <SimpleHeader headerText="Choose login" />,
            headerBackground: () => <BoschBottomStrip />,
        });
    }

    render() {
        const {
            container,
            maskViewContainer,
.
.
.
.

And now I can set header from component where I used SimpleHeader.

Petrik-a-dost
  • 223
  • 4
  • 11
1

componentDid worked for me. With stateless component:


const DefaultLogin = ({ navigation }) => {
  useEffect(() => {
    navigation.setOptions({
      headerTitle: () => <SimpleHeader headerText="Choose login" />,
      headerBackground: () => <BoschBottomStrip />,
    });
  }, [navigation]);

  return (
    // ...
  )
}

bechir
  • 334
  • 6
  • 13
0

Another solution, is to not call navigation.setOptions inside a child screen, but instead move that into the options of the screen in the parent component.

So previously we had:

Parent:

<NavigationContainer>
  <Stack.Navigator>
    <Stack.Screen
      name="Root"
      component={BottomTabNavigator}
    />
  </Stack.Navigator>
</NavigationContainer>

and child (BottomTabNavigator.jsx):

const BottomTabNavigator = ({ navigation }) => {
  navigation.setOptions({
    headerShown: false,
  });
}

Now we can just remove the setOptions call in the child, and put it into the parent:

<NavigationContainer>
  <Stack.Navigator>
    <Stack.Screen
      name="Root"
      component={BottomTabNavigator}
      options={{
        headerShown: false,
      }}
    />
  </Stack.Navigator>
</NavigationContainer>
fredrivett
  • 5,419
  • 3
  • 35
  • 48