如何在 React Native 应用程序中使用 React hook useEffect 为每 5 秒渲染设置间隔?

Posted

技术标签:

【中文标题】如何在 React Native 应用程序中使用 React hook useEffect 为每 5 秒渲染设置间隔?【英文标题】:How to setInterval for every 5 second render with React hook useEffect in React Native app? 【发布时间】:2019-12-23 19:39:14 【问题描述】:

我有React Native 应用程序,我通过fetchAPI 获取数据。我创建了从API 获取数据的自定义钩子。我需要每 5 秒重新渲染一次。为此,我将自定义钩子包装到 setInterval 并且在我的应用程序变得非常缓慢并且当我导航到另一个屏幕时出现此错误:

无法对未安装的组件执行 React 状态更新。这是 无操作,但它表明您的应用程序中存在内存泄漏。修理, 在 useEffect 清理中取消所有订阅和异步任务 功能。

你能告诉我如何解决这个错误,哪个是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>
  )


【问题讨论】:

【参考方案1】:

你需要清除你的interval

useEffect(() => 
  const intervalId = setInterval(() =>   //assign interval to a variable 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 => 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
 
, [url, useState])

有关useEffect 中的cleanup 函数的更多信息,请参阅this。

【讨论】:

我添加了这 2 行,但无论如何都会出现错误并且应用程序运行非常缓慢。 什么是错误?由于 setInterval 时间,应用程序工作缓慢,每次它会等待 5 秒。尽量减少时间。 无法对未安装的组件执行 React 状态更新。这是一个空操作,但它表明您的应用程序中存在内存泄漏。要解决此问题,请在 useEffect 清理函数中取消所有订阅和异步任务。 您的应用中是否还有其他 setInterval?通过这样做return () =&gt; clearInterval(intervalId);,它将清除 setInterval。 因为我想用来卸载组件。我删除了它,现在效果更好了。太感谢了。请再告诉我一件事,这个组件在我的 tabsNavigator 的一个选项卡中呈现,当我更改选项卡组件时没有卸载,呈现仍在工作。只有当我离开导航堆栈时,组件才会卸载。为什么?并且错误仍然出现,但不像以前那样经常出现【参考方案2】:

可能是这两种情况:

您需要清除间隔 如果 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])

根据您的服务器响应时间,可能需要为待处理的查询添加故障保护(例如,如果您发送了一个查询并且下一个查询在第一个查询返回之前启动......)。

【讨论】:

【参考方案3】:

让 React Hooks + Apollo 每 5 秒从 GraphQL 服务器获取数据。 在这个例子中,如果用户没有在后端登录,我们在 React 中注销用户。 (JWT 令牌不再有效)

import React from 'react'
import gql from 'graphql-tag'
import  useApolloClient  from '@apollo/react-hooks'

export const QUERY = gql`
  query Me 
    me 
      id
    
  
`

const MyIdle = () => 
  const client = useApolloClient()

  React.useEffect(() => 
    async function fetchMyAPI() 
      try 
        await client.query(
          query: QUERY,
          fetchPolicy: 'no-cache',
        )
       catch (e) 
        // Logout the user and redirect to the login page
      
    

    const intervalId = setInterval(() => 
      fetchMyAPI()
    , 1000 * 5) // in milliseconds
    return () => clearInterval(intervalId)
  , [client])

  return null

export default MyIdle

【讨论】:

以上是关于如何在 React Native 应用程序中使用 React hook useEffect 为每 5 秒渲染设置间隔?的主要内容,如果未能解决你的问题,请参考以下文章

React Native - 如何在 React Native 应用程序中使用 WebView 显示我的网站通知(适用于 Android)

如何在 react-native 中定位移动应用的用户?

如何在 react-native-map 中放大/缩小?

如何在 React Native 中使用 iframe?

如何使用 DrawerLayoutAndroid 组件在 react-native 应用程序中构建抽屉?

如何使用 Redux 在 React Native 中使用线程调用 API?