简体   繁体   English

如何在 React Native 应用程序中使用 React hook useEffect 每 5 秒渲染一次 setInterval?

[英]How to setInterval for every 5 second render with React hook useEffect in React Native app?

I have React Native app and I get data from API by fetch .我有React Native应用程序,我通过fetchAPI获取数据。 I created custom hook that get data from API .我创建了从API获取数据的自定义钩子。 And i need to re-render it every 5 seconds.我需要每 5 秒重新渲染一次。 For it I wrapped my custom hook to setInterval and after my app become work very slowly and when I navigate to another screen I get this error:为此,我将我的自定义钩子包装到 setInterval 并且在我的应用程序变得非常缓慢并且当我导航到另一个屏幕时我收到此错误:

Can't perform a React state update on an unmounted component.无法对卸载的组件执行 React 状态更新。 This is a no-op, but it indicates a memory leak in your application.这是一个空操作,但它表明您的应用程序中存在内存泄漏。 To fix, cancel all subscriptions and asynchronous tasks in a useEffect cleanup function.要修复,请取消 useEffect 清理函数中的所有订阅和异步任务。

Can you tell me please how can I solve this bug and which will be the best way to setInterval , because I think my way is not good.你能告诉我如何解决这个错误,哪种是最好的setInterval ,因为我认为我的方法不好。

My custom hook:我的自定义钩子:

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
}

My Component:我的组件:

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>
  )
}

You need to clear your interval ,你需要清除你的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])

For more about cleanup functions in useEffect refer to this .有关useEffect cleanup功能的更多信息,请参阅

It might be both these things:可能是这两件事:

  • You need to clear up your interval你需要清理你的间隔
  • You need to not update state from your API callback if its unmounted.如果已卸载,则无需从 API 回调更新状态。

Code:代码:

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])

Might want, depending on your server response time, add a failsafe for pending queries (example, if you sent out a query and the next one launches before the first one returns...).可能需要,根据您的服务器响应时间,为挂起的查询添加故障保护(例如,如果您发出查询并且下一个在第一个返回之前启动......)。

For React Hooks + Apollo to fetch data from a GraphQL server every 5 seconds. React Hooks + Apollo 每 5 秒从 GraphQL 服务器获取数据。 In this example, we logout the user in React if the user is not logged-in in the backend.在这个例子中,如果用户没有在后端登录,我们在 React 中注销用户。 (JWT token not valide anymore) (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

`const [time, setTime] = useState(0)

useEffect(()=>{
    setInterval(()=>{
        setTime(prev => prev + 1)
    }, 1000)
},[])`

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

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