Search code examples
javascriptreact-nativereact-contextuse-context

React Context returning undefined with useContext


I have context set up within my app. not at the root and for some reason it returns undefined.

I have this as the index where I create the context:

import React, { createContext, useContext, useReducer } from 'react'

type Action = { type: 'next' } | { type: 'back' }
type Dispatch = (action: Action) => void
type State = { step: number }
type StepProviderProps = { children: React.ReactNode }

const StepContext = createContext<{ state: State; dispatch: Dispatch } | undefined>(undefined)
const stepReducer = (state: State, action: Action) => {
  switch (action.type) {
    case 'next': {
      return { step: state.step + 1 }
    }
    case 'back': {
      return { step: state.step - 1 }
    }
    default: {
      throw new Error(`Unhandled action type`)
    }
  }
}

const StepProvider = ({ children }: StepProviderProps) => {
  const [state, dispatch] = useReducer(stepReducer, { step: 0 })
  const value = { state, dispatch }
  return <StepContext.Provider value={value}>{children}</StepContext.Provider>
}

const useStep = () => {
  const context = useContext(StepContext)
  if (context === undefined) {
    throw new Error('useStep should be used within StepProvider')
  } else {
    return context
  }
}

export { StepProvider, useStep }

then I import it here:

import { BodyText, H3, H5, H4 } from '../../typography'
import * as React from 'react'
import { Box, Icon } from '../../components'
import { useState } from 'react'
import { StyleSheet, TouchableOpacity, View } from 'react-native'
import { useNavigation } from '@react-navigation/native'
import { useAuthContext } from '../../context/AuthProvider'
import { Enum_Userspermissionsuser_Userrole } from '../../generated/graphql'
import Start from './Start'
import stepOne from './StepOne'
import { StepProvider, useStep } from './StepContext'

const OnboardingScreen = () => {
  const [stepper, setStepper] = useState([0, 1, 2, 3, 4, 5])

  const { navigate } = useNavigation()
  const { user } = useAuthContext()
  const {state:{step}} = useStep()
 

  return (
    <StepProvider>
      <Container px={5} justifyContent="flex-start" downbg hasNavbar={false}>
        <View style={{ flexDirection: 'row', justifyContent: 'space-evenly', marginTop:'12%'}}>
          {stepper.map((step, i) => (
            <Box height={5} width={50} bg="b2gpeach" borderRadius={11} key={i} />
          ))}
        </View>
        <Start />
        {console.log(step)}
        
      </Container>
      </StepProvider>
  )
}



export default OnboardingScreen

I think I should be able to get console log of the context but I get the context undefined error I set to check if context is defined.


Solution

  • You must use useContext within components wrapped with provider:

    <StepProvider>
      <OnboardingScreen />
    </StepProvider>
    

    So, you could do something like this:

    import { BodyText, H3, H5, H4 } from '../../typography'
    import * as React from 'react'
    import { Box, Icon } from '../../components'
    import { useState } from 'react'
    import { StyleSheet, TouchableOpacity, View } from 'react-native'
    import { useNavigation } from '@react-navigation/native'
    import { useAuthContext } from '../../context/AuthProvider'
    import { Enum_Userspermissionsuser_Userrole } from '../../generated/graphql'
    import Start from './Start'
    import stepOne from './StepOne'
    import { StepProvider, useStep } from './StepContext'
    
    const OnboardingScreen = () => {
      const [stepper, setStepper] = useState([0, 1, 2, 3, 4, 5])
    
      const { navigate } = useNavigation()
      const { user } = useAuthContext()
      const {state:{step}} = useStep()
     
      // Provider removed
      return (
        <Container px={5} justifyContent="flex-start" downbg hasNavbar={false}>
          <View style={{ flexDirection: 'row', justifyContent: 'space-evenly', marginTop:'12%'}}>
          {stepper.map((step, i) => (
            <Box height={5} width={50} bg="b2gpeach" borderRadius={11} key={i} />
          ))}
          </View>
          <Start />
          {console.log(step)}
        </Container>
      )
    }
    
    // Export wrapped with provider
    export default () => (
      <StepProvider>
        <OnboardingScreen />
      </StepProvider>
    )