web-dev-qa-db-ja.com

ReactネイティブアプリでReactフックユースエフェクタを使用して5秒ごとにSetIntervalを設定しますか?

APIによって_React Native_アプリとfetchからデータを取得します。 APIからデータを取得するカスタムフックを作成しました。そして私は5秒ごとにそれを再レンダリングする必要があります。それは私のカスタムフックをSetIntervalに包んだし、私のアプリが非常にゆっくり仕事をすると、私が別の画面に移動すると、このエラーが発生します。

マウント解除されていないコンポーネントでReact状態更新を実行できません。これはNO OPですが、アプリケーションのメモリリークを示します。修正するには、UseEffect Cleanup関数ですべてのサブスクリプションと非同期タスクをキャンセルします。

私はこのバグを解決する方法を教えてください。私はsetIntervalに最適な方法になるでしょう。

私のカスタムフック:

_export const useFetch = url => {
  const [state, setState] = useState({ data: null, error: false, loading: true })

  useEffect(() => {
    setInterval(() => {
      setState(state => ({ data: state.data, error: false, loading: true }))
      fetch(url)
        .then(data => data.json())
        .then(obj =>
          Object.keys(obj).map(key => {
            let newData = obj[key]
            newData.key = key
            return newData
          })
        )
        .then(newData => setState({ data: newData, error: false, loading: false }))
        .catch(function(error) {
          console.log(error)
          setState({ data: null, error: true, loading: false })
        })
    }, 5000)
  }, [url, useState])
  useEffect(() => () => console.log('unmount'), [])
  return state
}
_

私のコンポーネント:

_const ChartsScreen = ({ navigation }) => {
  const { container } = styles
  const url = 'https://poloniex.com/public?command=returnTicker'
  const { data, error, loading } = useFetch(url)

  const percentColorHandler = number => {
    return number >= 0 ? true : false
  }

  return (
    <View style={container}>
      <ProjectStatusBar />
      <IconsHeader
        dataError={false}
        header="Charts"
        leftIconName="ios-arrow-back"
        leftIconPress={() => navigation.navigate('Welcome')}
      />
      <ChartsHeader />
      <ActivityIndicator animating={loading} color="#068485" style={{ top: HP('30%') }} size="small" />
      <FlatList
        data={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>
  )
}

_
6
jocoders

それは両方のことかもしれません:

  • あなたはあなたの間隔を消す必要があります
  • マウント解除されていない場合は、APIコールバックから状態を更新する必要があります。

コード:

useEffect(() => {
   let isMounted = true
   const intervalId = setInterval(() => {  //assign interval to a variaable to clear it
    setState(state => ({ data: state.data, error: false, loading: true }))
    fetch(url)
      .then(data => data.json())
      .then(obj =>
        Object.keys(obj).map(key => {
          let newData = obj[key]
          newData.key = key
          return newData
        })
     )
     .then(newData => {
        if(!isMounted) return  // This will cancel the setState when unmounted
        setState({ data: newData, error: false, loading: false })
     })
     .catch(function(error) {
        console.log(error)
        setState({ data: null, error: true, loading: false })
     })
   }, 5000)

   return () => {
       clearInterval(intervalId); //This is important
       isMounted = false // Let's us know the component is no longer mounted.
   }

}, [url, useState])
 _

サーバーの応答時間に応じて、保留中のクエリにフェイルセーフを追加したい場合があります(例、最初の1つの戻り値の前に次の起動が行われた場合は例)。

2
denislexic