写一个关于RN的分秒毫秒组件(组件状态由同一个父组件控制)

介绍一下,就一个界面会一直跑时间,项目有个需求需要用到毫秒级计时器,那我肯定想到用组件了塞,但是组件的状态和组件的数据都是不互通的都是独立的,因此我写了下面这个组件,组件的状态会由父组件控制切记,必须是同一个父组件,因为状态是父组件控制的,如果是多个页面多个父组件的话可以不用看,一键控制所有的组件的开启或者暂停,也可以控制所有组件是否重新启动重新计时,当前也考虑到了如果组件不是同时出现这个数据怎么保持一致的这个方案,组件代码如下

js 复制代码
import React, { useState, useEffect, useRef } from 'react'
import { Text, View, Button, StyleSheet } from 'react-native'

const Timer = ({ isRunning, elapsedTime, onStart, onStop, firstElapsedTime }) => {
  const startTimeRef = useRef(null)
  const animationIdRef = useRef(null)
  const previousTimeRef = useRef(0)

  const startTimer = () => {
    startTimeRef.current = Date.now() - elapsedTime
    animationIdRef.current = requestAnimationFrame(updateTimer)
  }

  const stopTimer = () => {
    cancelAnimationFrame(animationIdRef.current)
  }

  const updateTimer = () => {
    const currentTime = Date.now()
    const elapsedTime = currentTime - startTimeRef.current
    previousTimeRef.current = elapsedTime
    onStart(elapsedTime)
    animationIdRef.current = requestAnimationFrame(updateTimer)
  }

  useEffect(() => {
    if (isRunning) {
      startTimer()
    } else {
      stopTimer()
    }

    return () => {
      stopTimer()
    }
  }, [isRunning])

  const handleControl = () => {
    if (isRunning) {
      onStop()
    } else {
      startTimer()
    }
  }

  const formatTime = (milliseconds) => {
    const minutes = Math.floor(milliseconds / (60 * 1000))
    const seconds = Math.floor((milliseconds % (60 * 1000)) / 1000)
    const millisecondsFraction = Math.floor((milliseconds % 1000) / 10)
    return (
      (minutes < 10 ? '0' : '') + minutes + ':' +
      (seconds < 10 ? '0' : '') + seconds + ':' +
      (millisecondsFraction < 10 ? '0' : '') + millisecondsFraction
    )
  }

  return (
    <View>
      <Text>{formatTime(firstElapsedTime)}</Text>
    </View>
  )
}

const ParentComponent = () => {
  const [timers, setTimers] = useState([
    { id: 1, isRunning: false, elapsedTime: 0 },
    { id: 2, isRunning: false, elapsedTime: 0 },
    { id: 3, isRunning: false, elapsedTime: 0 },
  ])
  const [nextId, setNextId] = useState(4)

  const handleStartAll = () => {
    setTimers(timers.map(timer => ({ ...timer, isRunning: true })))
  }

  const handlePauseAll = () => {
    setTimers(timers.map(timer => ({ ...timer, isRunning: false })))
  }

  const handleContinueAll = () => {
    setTimers(timers.map(timer => ({ ...timer, isRunning: true })))
  }

  const handleRestartAll = () => {
    handlePauseAll()
    setTimeout(() => {
      setTimers(timers.map(timer => ({ ...timer, isRunning: true, elapsedTime: timers[0].elapsedTime })))
    }, 100)
  }

  const handleAddTimer = () => {
    const firstTimerElapsedTime = timers[0].elapsedTime // 获取第一个计时器组件的时间
    const newTimer = { id: nextId, isRunning: false, elapsedTime: firstTimerElapsedTime }
    setNextId(nextId + 1)
    setTimers([...timers, newTimer])
  }

  return (
    <View style={styles.container}>
      <View style={styles.timerContainer}>
        {timers.map(timer => (
          <Timer
            key={timer.id}
            isRunning={timer.isRunning}
            elapsedTime={timer.elapsedTime}
            onStart={elapsedTime => {
              const newTimers = [...timers]
              const index = newTimers.findIndex(t => t.id === timer.id)
              newTimers[index].elapsedTime = elapsedTime
              setTimers(newTimers)
            }}
            onStop={() => {
              const newTimers = [...timers]
              const index = newTimers.findIndex(t => t.id === timer.id)
              newTimers[index].isRunning = false
              setTimers(newTimers)
            }}
            firstElapsedTime={timers[0].elapsedTime} // 传递第一个计时器的elapsedTime给Timer组件
          />
        ))}
      </View>
      <View style={styles.buttonContainer}>
        <Button title={timers.some(timer => timer.isRunning) ? '全部暂停' : '全部开始'} onPress={timers.some(timer => timer.isRunning) ? handlePauseAll : handleStartAll} />
        <Button title="继续" onPress={handleContinueAll} />
        <Button title="重新启动" onPress={handleRestartAll} />
        <Button title="添加新组件(需要先暂停才行,时间最初为00:00:00,点击开始后时间以第一个组件时间为准)" onPress={handleAddTimer} />
      </View>
    </View>
  )
}

const styles = StyleSheet.create({
  container: {
    flex: 1,
    justifyContent: 'center',
    alignItems: 'center',
  },
  timerContainer: {
    marginBottom: 20,
  },
  buttonContainer: {
    flexDirection: 'column',
    justifyContent: 'space-around',
    marginTop: 20,
  },
})

export default ParentComponent

上面的组件说明下:(时间会有差别,但是差别不大,基本在几毫秒之内,为了保持数据的一致性,我将都参照第一个组件的时间为准,点击暂停时,所有组件的时间都跟第一个组件保持一致) 点击开始:所有组件开始计时 点击暂停:所有组件全部暂停 点击重启:计时从0开始,重新计时 点击新增组件:需要先暂停才行,因为页面一直在渲染添加不上,我没解决掉,有大佬能解决了更好,还有就是时间以第一个组件显示的时间为准,包括暂停也都是(为了保证所有组件计时的时间的同步)

PS:上面的那个组件展示没必要用循环奥,直接按照下面写也行【如果需要用循环写,也可用循环】

js 复制代码
<Timer
  key={timers[0].id}
  isRunning={timers[0].isRunning}
  elapsedTime={timers[0].elapsedTime}
  onStart={elapsedTime => {
  const newTimers = [...timers]
  const index = newTimers.findIndex(t => t.id === timers[0].id)
      newTimers[index].elapsedTime = elapsedTime
            setTimers(newTimers)
      }}
   onStop={() => {
        const newTimers = [...timers]
        const index = newTimers.findIndex(t => t.id === timers[0].id)
         newTimers[index].isRunning = false
              setTimers(newTimers)
         }}
   firstElapsedTime={timers[0].elapsedTime} // 传递第一个计时器的elapsedTime给Timer组件
                />

上面代码在需要用到组件的地方直接用就行,相当于每个组件都用第一个的状态,或者每个都用自己的状态其实效果是一样的,这种是对于不需要循环在某一个地方使用的情况,就算是循环直接循环三个这样的也行,总之就是这样写也可以,用循环写也可以,这个各位自己定

相关推荐
子兮曰3 天前
jev-ultrafast 深度解析:7 秒订机票的浏览器 Agent 是如何炼成的
前端·后端·agent
子兮曰3 天前
Jev 爆发一周:7 秒 Agent 背后的 System One 生态与三场争议
前端·后端·ai编程
前端小万3 天前
写公众号赚了 3000 块后,我做了一款叫 "一键成稿" 的软件
前端·微信小程序
爱勇宝3 天前
ZCode 开源 24 小时:一份没有历史的账本,回答不了"有没有偷代码"
前端·后端·chatglm (智谱)
三十而立洋3 天前
Cookie 详解:从产生到安全,一次讲透
前端·javascript
卡布鲁3 天前
把一个 Vite + Vue3 应用塞进 qiankun (React + Umi3) 主站:十个坑的复盘
前端·javascript·react.js
汉堡大王95273 天前
Jev:不是聊天机器人, 而是一个智能 if 语句
前端·人工智能·后端
梦想很大很大3 天前
从运行事实到回归证据:Workrun 的 Telemetry 与 Evaluation 实践
前端·人工智能·后端
计算机魔术师3 天前
Meta Muse agent 接入 Shopify 的 Shop Pay 实现代理式购物
前端
沙蒿同学3 天前
我用 Go 搭了一条 AI Agent 流水线:从 1 张商品图到一整套淘宝详情页
前端·javascript·后端