Search code examples
react-nativemobx

invalid hook call in mobx+react native


I'm new to mobx,

I was told that I can't use directly rootStore from rootStore.tsx directly, and I have to replace it with hook, so I've tried to call hook useStore from rootStore.tsx

but in this case I've got an error "invalid hook call. Hooks can be called inside of the body"

my files are:

rootStore.tsx

import { createContext, useContext } from 'react'
import { makeAutoObservable } from 'mobx'
import { AsyncTrunk } from 'mobx-sync'
import AsyncStorage from '@react-native-async-storage/async-storage'
import { DayStyle, firstDayStyle } from '../styles/markedDayStyle'


const period: Record<string, DayStyle> = {
  '2022-02-16': firstDayStyle,
}


export const rootStore = makeAutoObservable({
  periods: period,
})

export const trunk = new AsyncTrunk(rootStore, {
  storage: AsyncStorage,
})
export const StoreContext = createContext(rootStore)
export const StoreProvider = StoreContext.Provider
export const useStore = () => useContext(StoreContext)

App.tsx

const App = observer(({}) => {
  const store = useStore()
  const [isStoreLoaded, setIsStoreLoaded] = useState(false)


  useEffect(() => {
    const rehydrate = async () => {
      await trunk.init()
      setIsStoreLoaded(true)
    }
    rehydrate().catch(() => console.log('problems with localStorage'))
  }, [store])

  if (!isStoreLoaded) {
    return (
      <View style={{ flex: 1, alignItems: 'center', justifyContent: 'center' }}>
        <ActivityIndicator size="large" />
      </View>
    )
  } else {
    return (
      <StoreProvider value={store}>
        <PaperProvider theme={store.isDarkMode ? darkTheme : lightTheme}>
          <View style={styles.container}>
            <CalendarScreen/>
          </View>
        </PaperProvider>
      </StoreProvider>
    )
  }
})


CalendarScreen.tsx

const CalendarScreen = observer(({}) => {
  const store = useStore()

  const handleDayPress = (day: DateData) => {
    setModalVisible(true)
    setPressedDay(day.dateString)
  }


  return (
    <SafeAreaView style={styles.screenContainer}>
       <Calendar 
        onDayPress={day => {handleDayPress(day)}}
        />
       <View>
          <ModalConfirmDay modalVisible={modalVisible} setModalVisible={setModalVisible} pressedDay={pressedDay} />
       </View>
    </SafeAreaView>
  )

)}


ModalConfirmDay.tsx

import { fillMarkedDays } from '../functions/fillMarkedDays'

const ModalConfirmDay = observer(({ modalVisible, setModalVisible, pressedDay }: ModalConfirmDayProps) => {


  const handlePeriodStarts = () => {
    fillMarkedDays(pressedDay)
    setModalVisible(false)
  }

  return (
    <View style={styles.centeredView}>
      <Modal
        visible={modalVisible}
      >
        <View style={styles.modalView}>
          <TouchableOpacity onPress={() => handlePeriodStarts()}>
            <Text>Period starts</Text>
          </TouchableOpacity>
        </View>
      </Modal>
    </View>
  )
})

fillMarkedDays.tsx


import { rootStore, useStore} from '../store/rootStore'
import { firstDayStyle} from '../styles/markedDayStyle'

const fillMarkedDays = (selectedDay: string) => {
  const store = useStore()
 

  if (selectedDay) {
    store.periods[selectedDay] = firstDayStyle
  }
}

when I try to add a new key-value (in fillMarkedDays.tsx) to store.periods I'm getting this

how can I fix this or select a better approach to call the store? Thanks everyone


Solution

  • By the rules of hooks you can't use hooks outside of the body of the function (component), so basically you can only use them before return statement, and also you can't use any conditions and so on. fillMarkedDays is just a function, not a component, it has no access to React context, hooks or whatever.

    What you can do is first get the store with hook, then pass it as an argument into the fillMarkedDays function:

    const ModalConfirmDay = observer(({ modalVisible, setModalVisible, pressedDay }: ModalConfirmDayProps) => {
      const store = useStore()
    
      const handlePeriodStarts = () => {
        fillMarkedDays(store, pressedDay)
        setModalVisible(false)
      }
      
    
      // ...
    }