简体   繁体   中英

Mobx, React Native, strange behavior of mobx mutation state after fetch data from API

I have React Native app with MobX store. And i use useEffect hook to call fetch action from MobX to get data from API. The rendering is pretty strange. It looks like this:

useEffect call MobX action with fetch -> loading data, but can not render, the loading is not stopping -> push the button and change the navigation stack -> the data is appearing on a previous screen where before it could not rendered -> come back to the previous screen and see the data that before could not came. It means only when the navigation stack is changing the data is rendering. It looks like a problem with change MobX state. Can you help me please.

MobX state:

import { createContext } from 'react'
import { action, decorate, observable, computed, runInAction } from 'mobx'
import fetchData from '../utils/fetchData'
import mapObjects from '../utils/mapObjects'

class DataStore {
  data = null
  error = false
  loading = true

  get getData(){
    return this.data
  }

  get getError(){
    return this.error
  }

  get getLoading(){
    return this.loading
  }

  async fetchData(url) {
  this.data = null
  this.error = false
  this.loading = true
    try {
      console.log('TRY')
      const response = await fetch(url)
      const jsonResponse = await response.json()
      const obj = await mapObjects(jsonResponse)
      runInAction(() => {
        console.log('WRITE!!!')
        this.loading = false
        this.data = obj
      })
    } catch (err) {
      runInAction(() => {
        console.log(err)
        this.loading = false
        this.error = err
      })
    }
  }
}

decorate(DataStore, {
  data: observable,
  error: observable,
  loading: observable,
  fetchData: action
})

export default createContext(new DataStore())

Render component:

import React, { useContext, useEffect, useState } from 'react'

import { ActivityIndicator, FlatList, Platform, StyleSheet, View } from 'react-native'
import DataStore from '../mobx/DataStore'
import { autorun } from 'mobx'
import { ChartsHeader, CryptoItem, IconsHeader, ProjectStatusBar } from '../components'
import { useFetch } from '../hooks/useFetch'
import { WP, HP } from '../constants'

const styles = StyleSheet.create({
  container: {
    flex: 1
  }
})
const ChartsScreen = ({ navigation }) => {
  const { container } = styles
  const store = useContext(DataStore)
  const url = 'https://poloniex.com/public?command=returnTicker'

  console.log('store', store)
  useEffect(() => {
    store.fetchData(url)
  }, [])
  //*Call custom hook and data distruction
  //const { data, error, loading } = useFetch(url)

  //*Change percent amount color depends on the amount
  const percentColorHandler = number => {
    return number >= 0 ? true : false
  }

  return (
    <View style={container}>
      {Platform.OS === 'ios' && <ProjectStatusBar />}
      <IconsHeader
        dataError={store.error}
        header="Charts"
        leftIconName="ios-arrow-back"
        leftIconPress={() => navigation.navigate('Welcome')}
      />
      <ChartsHeader />
      <ActivityIndicator animating={store.loading} color="#068485" style={{ top: HP('30%') }} size="small" />
      <FlatList
        data={store.data}
        keyExtractor={item => item.key}
        renderItem={({ item }) => (
          <CryptoItem
            name={item.key}
            highBid={item.highestBid}
            lastBid={item.last}
            percent={item.percentChange}
            percentColor={percentColorHandler(item.percentChange)}
          />
        )}
      />
    </View>
  )
}

export { ChartsScreen }

In my case it was, because i put all fetch functions to the one hook and call it in useEffect. In the end i have found the decision. I changes my function component to the class component and split all fetch functions in MobX store. Maybe it will be helpful for somebody: MobX store:

import { action, observable, runInAction } from 'mobx'

class DataStore {
  @observable data = null
  @observable error = false
  @observable fetchInterval = null
  @observable loading = false

  //*Make request to API
  @action.bound
  fetchInitData() {
    const response = fetch('https://poloniex.com/public?command=returnTicker')
    return response
  }

  //*Parse data from API
  @action.bound
  jsonData(data) {
    const res = data.json()
    return res
  }

  //*Get objects key and push it to every object
  @action.bound
  mapObjects(obj) {
    const res = Object.keys(obj).map(key => {
      let newData = obj[key]
      newData.key = key
      return newData
    })
    return res
  }

  //*Main bound function that wrap all fetch flow function
  @action.bound
  async fetchData() {
    try {
      runInAction(() => {
        this.error = false
        this.loading = true
      })
      const response = await this.fetchInitData()
      const json = await this.jsonData(response)
      const map = await this.mapObjects(json)
      const run = await runInAction(() => {
        this.loading = false
        this.data = map
      })
    } catch (err) {
      console.log(err)
      runInAction(() => {
        this.loading = false
        this.error = err
      })
    }
  }

  //*Call reset of MobX state
  @action.bound
  resetState() {
    runInAction(() => {
      this.data = null
      this.fetchInterval = null
      this.error = false
      this.loading = true
    })
  }

  //*Call main fetch function with repeat every 5 seconds
  //*when the component is mounting
  @action.bound
  initInterval() {
    if (!this.fetchInterval) {
      this.fetchData()
      this.fetchInterval = setInterval(() => this.fetchData(), 5000)
    }
  }

  //*Call reset time interval & state
  //*when the component is unmounting
  @action.bound
  resetInterval() {
    if (this.fetchInterval) {
      clearTimeout(this.fetchInterval)
      this.resetState()
    }
  }
}

const store = new DataStore()
export default store

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM