0

I'm new to react native concepts. I'm trying to pass functions and a parameter to context API so that I can access those in my child component. I'm trying to implement basic login functionality that will show different messages based on user login status. The functionality works when I pass the SignIn method to my child component but the same function is not accessible when I send it along with some variable. The below code and notes can explain the problem clearly.

In the below code as you can see I'm passing my reducer function and initial states from which I get the error messages and sign function <AuthenticationContext.Provider value={[authContext, initialLoginState]}>

App.js

import * as React from 'react';
import { NavigationContainer } from '@react-navigation/native';
import BottomNavigation from './src/Navigations/BottomNavigation';
import AuthStackNavigation from './src/Navigations/AuthStackNavigation'
import { useEffect, useMemo, useReducer } from 'react';
import { View } from 'react-native-animatable';
import apiCaller from "./src/api/apiCaller";
import { ActivityIndicator, Text } from 'react-native';
import { AuthenticationContext } from './src/context/AuthenticationContext'
import { Provider as VideoProvider } from './src/context/videoContext'
import { Context as VideoContext } from './src/context/videoContext'
import AsyncStorage from '@react-native-async-storage/async-storage'

export default function App() {

  //Initial state values
  const initialLoginState = {
    isLoading: true,
    userName: null,
    userToken: null,
    errorMessage: ''
  }

  //Reducer function
  const loginReducer = (prevState, action) => {
    switch (action.type) {
      case 'LOGIN':
        return {
          ...prevState,
          userToken: action.token,
          userName: action.id,
          isLoading: false
        };
      case 'LOGOUT':
        return {
          ...prevState,
          userName: null,
          userToken: null,
          isLoading: false,
        };
      case 'REGISTER':
        return {
          ...prevState,
          userToken: action.token,
          userName: action.id,
          isLoading: false,
        };
      case 'ERROR':
        return {
          ...prevState,
          errorMessage: action.response,
          isLoading: false
        };
      default:
        return { userToken: null }
    }
  }

  //Defining useReducer
  const [newLoginState, dispatch] = useReducer(loginReducer, initialLoginState);

  const authContext = useMemo(() => ({
    signIn: async (email, password) => {
      try {
        const userData = {
          email: email,
          password: password,

        };
        const response = await apiCaller.post("/login", userData);
        if (response.data.code == '200') {
          await AsyncStorage.setItem('userToken', response.data.token)
          dispatch({ type: 'LOGIN', id: email, token: response.data.token })
        }
        else if (response.data.code == '404') {
          dispatch({ type: 'ERROR', response: response.data.message })
        }
      } catch (err) {
        console.log(err);
      }
    }
  }), []);


  return (
    <VideoProvider value={VideoContext}>
      <AuthenticationContext.Provider value={{authContext, initialLoginState}}>
        <NavigationContainer>
          {newLoginState.userToken == null ?
            <AuthStackNavigation />
            :
            <BottomNavigation />
          }
        </NavigationContainer>
      </AuthenticationContext.Provider>
    </VideoProvider>
  );
}

and in the below file, I'm fetching the provider vales by

const { signIn, initialLoginState } = useContext(AuthenticationContext) but its giving me "signIn is not a function. (In 'signIn(email, password)', 'signIn' is undefined)" error but signIn method is accessible when i just try to pass and access SignIn method alone.

Signinscreen.js

import React, { useState } from 'react'
import { Ionicons, MaterialIcons } from '@expo/vector-icons';
import { Text, View, StyleSheet, TouchableOpacity, Platform, StatusBar } from 'react-native';
import { WindowHeight, WindowWidth } from '../utils/PlatformDimention'
import PrimaryFormInput from "../components/PrimaryFormInput";
import PrimaryFormButton from "../components/PrimaryFormButton";

import { AuthenticationContext } from "../context/AuthenticationContext";
import { useContext } from 'react';

const UserLogin = ({ navigation }) => {

    const [email, setEmail] = useState('');
    const [password, setPassword] = useState('');
    const { signIn, initialLoginState } = useContext(AuthenticationContext)

    const loginHandle = (email, password) => {
        signIn(email, password);
    }

    return (
        <View style={styles.container}>
            <StatusBar barStyle='light-content' />
            <View style={styles.header}>
                <View style={{ flex: 1, }}>
                    <TouchableOpacity onPress={() => navigation.goBack()}>
                        <MaterialIcons style={styles.goBack} name="arrow-back" size={WindowHeight / 27} />
                    </TouchableOpacity>
                </View>
                <View style={{ flex: 2, alignItems: 'center', justifyContent: 'center', }}>
                    <Ionicons
                        style={{ color: "#fff" }} name="logo-bitbucket" size={WindowHeight * 10 / 100} />
                </View>
                <View style={{ flex: 1, alignItems: 'flex-end' }}>

                </View>
            </View>
            <View style={styles.footer}>
                <View style={styles.topFlex}></View>
                <View style={styles.middleFlex}>
                    <Text style={styles.loginText}>Welcome!</Text>
                    <Text style={styles.loginSubTextSub}>Login to your existing account.</Text>
                    <PrimaryFormInput
                        inputValue={email}
                        onChangeText={(userEmail) => setEmail(userEmail)}
                        inputPlaceHolder='Email'
                        iconName="user"
                        keyboardType="email-address"
                        autoCapitalize="none"
                        autoCorrect={false}
                    />
                    <PrimaryFormInput
                        inputValue={password}
                        onChangeText={(userPassword) => setPassword(userPassword)}
                        inputPlaceHolder='Password'
                        iconName="key"
                        secureTextEntry={true}
                    />
                    <PrimaryFormButton
                        ButtonText='Login'
                        onPress={() => loginHandle(email, password)}
                    />
                </View>
                <View style={styles.bottomFlex}></View>
            </View>
        </View>
    )
}

export default UserLogin

Could anyone of you please let me know how to pass both method and parameters to my child component and access it?

1 Answer 1

1

Your mistake is just here

const { signIn, initialLoginState } = useContext(AuthenticationContext)

Because you pass the context object like this

<AuthenticationContext.Provider value={{authContext, initialLoginState}}>

You need to do the destructruring like this

const { authContext : { signIn }, initialLoginState } = useContext(AuthenticationContext)
Sign up to request clarification or add additional context in comments.

2 Comments

Thanks a lot for your quick response. I was able to pass the parameter easily after destructing but it shows an empty on my Singin screen even though I'm updating the errorMessage parameter through the reducer function. Any idea on what could be the reason?
please ignore my above comment. I was able to get the latest value from the reducer.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.