gpt4 book ai didi

reactjs - 使用 setTimeout 延迟在 useEffect 中 react setInterval

转载 作者:行者123 更新时间:2023-12-04 01:25:35 24 4
gpt4 key购买 nike

我想在它第一次触发时运行一个延迟间隔。如何使用 useEffect 做到这一点?由于语法,我发现很难实现我想做的事情

区间函数

  useEffect(()=>{
const timer = setInterval(() => {
//do something here
return ()=> clearInterval(timer)
}, 1000);
},[/*dependency*/])

延迟函数
useEffect(() => {
setTimeout(() => {
//I want to run the interval here, but it will only run once
//because of no dependencies. If i populate the dependencies,
//setTimeout will run more than once.
}, Math.random() * 1000);
}, []);

当然这是可以实现的......

最佳答案

入门
考虑解开组件的关注点并编写小片段。这里我们有一个 useInterval自定义 Hook ,严格定义 setInterval程序的一部分。我添加了一些 console.log线,所以我们可以观察效果 -

// rough draft
// read on to make sure we get all the parts right
function useInterval (f, delay)
{ const [timer, setTimer] =
useState(null)

const start = () =>
{ if (timer) return
console.log("started")
setTimer(setInterval(f, delay))
}

const stop = () =>
{ if (!timer) return
console.log("stopped", timer)
setTimer(clearInterval(timer))
}

useEffect(() => stop, [])

return [start, stop, timer != null]
}
现在当我们写 MyComp我们可以处理 setTimeout程序的一部分——
function MyComp (props)
{ const [counter, setCounter] =
useState(0)

const [start, stop, running] =
useInterval(_ => setCounter(x => x + 1), 1000) // first try

return <div>
{counter}
<button
onClick={start}
disabled={running}
children="Start"
/>
<button
onClick={stop}
disabled={!running}
children="Stop"
/>
</div>
}
现在我们可以 useInterval在我们程序的各个部分中,每个部分都可以不同地使用。启动、停止和清理的所有逻辑都很好地封装在钩子(Hook)中。
这是一个演示,您可以运行它来查看它的工作原理 -

const { useState, useEffect } = React

const useInterval = (f, delay) =>
{ const [timer, setTimer] =
useState(undefined)

const start = () =>
{ if (timer) return
console.log("started")
setTimer(setInterval(f, delay))
}

const stop = () =>
{ if (!timer) return
console.log("stopped", timer)
setTimer(clearInterval(timer))
}

useEffect(() => stop, [])

return [start, stop, timer != null]
}

const MyComp = props =>
{ const [counter, setCounter] =
useState(0)

const [start, stop, running] =
useInterval(_ => setCounter(x => x + 1), 1000)

return <div>
{counter}
<button
onClick={start}
disabled={running}
children="Start"
/>
<button
onClick={stop}
disabled={!running}
children="Stop"
/>
</div>
};


ReactDOM.render
( <MyComp/>
, document.getElementById("react")
)
<div id="react"></div>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/16.13.1/umd/react.production.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/16.13.1/umd/react-dom.production.min.js"></script></script>


做对了
我们要确保我们的 useInterval如果我们的计时器停止或在我们的组件被移除后,钩子(Hook)不会让任何定时函数运行。让我们在一个更严格的例子中测试它们,我们可以添加/删除许多计时器并随时启动/停止它们 -
add/remove timers
需要对 useInterval 进行一些基本更改。 -
function useInterval (f, delay = 1000)
{ const [busy, setBusy] = useState(0)

useEffect(() => {
// start
if (!busy) return
setBusy(true)
const t = setInterval(f, delay)
// stop
return () => {
setBusy(false)
clearInterval(t)
}
}, [busy, delay])

return [
_ => setBusy(true), // start
_ => setBusy(false), // stop
busy // isBusy
]
}
使用 useIntervalMyTimer组件很直观。 MyTimer不需要对间隔进行任何类型的清理。清理由 useInterval 自动处理-
function MyTimer ({ delay = 1000, auto = true, ... props })
{ const [counter, setCounter] =
useState(0)

const [start, stop, busy] =
useInterval(_ => {
console.log("tick", Date.now()) // <-- for demo
setCounter(x => x + 1)
}, delay)

useEffect(() => {
console.log("delaying...") // <-- for demo
setTimeout(() => {
console.log("starting...") // <-- for demo
auto && start()
}, 2000)
}, [])

return <span>
{counter}
<button onClick={start} disabled={busy} children="Start" />
<button onClick={stop} disabled={!busy} children="Stop" />
</span>
}
Main组件没有做任何特别的事情。它只管理 MyTimer 的数组状态组件。不需要特定于计时器的代码或清理 -
const append = (a = [], x = null) =>
[ ...a, x ]

const remove = (a = [], x = null) =>
{ const pos = a.findIndex(q => q === x)
if (pos < 0) return a
return [ ...a.slice(0, pos), ...a.slice(pos + 1) ]
}

function Main ()
{ const [timers, setTimers] = useState([])

const addTimer = () =>
setTimers(r => append(r, <MyTimer />))

const destroyTimer = c => () =>
setTimers(r => remove(r, c))

return <main>
<button
onClick={addTimer}
children="Add Timer"
/>
{ timers.map((c, key) =>
<div key={key}>
{c}
<button
onClick={destroyTimer(c)}
children="Destroy"
/>
</div>
)}
</main>
}
展开下面的片段以查看 useInterval在您自己的浏览器中工作。此演示建议使用全屏模式 -

const { useState, useEffect } = React

const append = (a = [], x = null) =>
[ ...a, x ]

const remove = (a = [], x = null) =>
{ const pos = a.findIndex(q => q === x)
if (pos < 0) return a
return [ ...a.slice(0, pos), ...a.slice(pos + 1) ]
}

function useInterval (f, delay = 1000)
{ const [busy, setBusy] = useState(0)

useEffect(() => {
// start
if (!busy) return
setBusy(true)
const t = setInterval(f, delay)
// stop
return () => {
setBusy(false)
clearInterval(t)
}
}, [busy, delay])

return [
_ => setBusy(true), // start
_ => setBusy(false), // stop
busy // isBusy
]
}

function MyTimer ({ delay = 1000, auto = true, ... props })
{ const [counter, setCounter] =
useState(0)

const [start, stop, busy] =
useInterval(_ => {
console.log("tick", Date.now())
setCounter(x => x + 1)
}, delay)

useEffect(() => {
console.log("delaying...")
setTimeout(() => {
console.log("starting...")
auto && start()
}, 2000)
}, [])

return <span>
{counter}
<button
onClick={start}
disabled={busy}
children="Start"
/>
<button
onClick={stop}
disabled={!busy}
children="Stop"
/>
</span>
}

function Main ()
{ const [timers, setTimers] = useState([])

const addTimer = () =>
setTimers(r => append(r, <MyTimer />))

const destroyTimer = c => () =>
setTimers(r => remove(r, c))

return <main>
<p>Run in expanded mode. Open your developer console</p>
<button
onClick={addTimer}
children="Add Timer"
/>
{ timers.map((c, key) =>
<div key={key}>
{c}
<button
onClick={destroyTimer(c)}
children="Destroy"
/>
</div>
)}
</main>
}

ReactDOM.render
( <Main/>
, document.getElementById("react")
)
<div id="react"></div>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/16.13.1/umd/react.production.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/16.13.1/umd/react-dom.production.min.js"></script></script>


进阶
让我们想象一个更复杂的 useInterval定时功能的场景, f ,以及 delay可以换 -
advanced timers
function useInterval (f, delay = 1000)
{ const [busy, setBusy] = // ...
const interval = useRef(f)

useEffect(() => {
interval.current = f
}, [f])

useEffect(() => {
// start
// ...
const t =
setInterval(_ => interval.current(), delay)

// stop
// ...
}, [busy, delay])

return // ...
}
现在我们可以编辑 MyTimer添加 doublerturbo状态 -
function MyTimer ({ delay = 1000, auto = true, ... props })
{ const [counter, setCounter] = useState(0)

const [doubler, setDoubler] = useState(false) // <--
const [turbo, setTurbo] = useState(false) // <--

const [start, stop, busy] =
useInterval
( doubler // <-- doubler changes which f is run
? _ => setCounter(x => x * 2)
: _ => setCounter(x => x + 1)
, turbo // <-- turbo changes delay
? Math.floor(delay / 2)
: delay
)

// ...

然后我们添加一个双涡轮增压按钮 -
  // ...
const toggleTurbo = () =>
setTurbo(t => !t)

const toggleDoubler = () =>
setDoubler(t => !t)

return <span>
{counter}
{/* start button ... */}
<button
onClick={toggleDoubler} // <--
disabled={!busy}
children={`Doubler: ${doubler ? "ON" : "OFF"}`}
/>
<button
onClick={toggleTurbo} // <--
disabled={!busy}
children={`Turbo: ${turbo ? "ON" : "OFF"}`}
/>
{/* stop button ... */}
</span>
}
展开下面的代码片段以在您自己的浏览器中运行高级计时器演示 -

const { useState, useEffect, useRef, useCallback } = React

const append = (a = [], x = null) =>
[ ...a, x ]

const remove = (a = [], x = null) =>
{ const pos = a.findIndex(q => q === x)
if (pos < 0) return a
return [ ...a.slice(0, pos), ...a.slice(pos + 1) ]
}

function useInterval (f, delay = 1000)
{ const interval = useRef(f)
const [busy, setBusy] = useState(0)

useEffect(() => {
interval.current = f
}, [f])

useEffect(() => {
// start
if (!busy) return
setBusy(true)
const t =
setInterval(_ => interval.current(), delay)

// stop
return () => {
setBusy(false)
clearInterval(t)
}
}, [busy, delay])

return [
_ => setBusy(true), // start
_ => setBusy(false), // stop
busy // isBusy
]
}

function MyTimer ({ delay = 1000, ... props })
{ const [counter, setCounter] =
useState(0)

const [doubler, setDoubler] = useState(false)
const [turbo, setTurbo] = useState(false)

const [start, stop, busy] =
useInterval
( doubler
? _ => setCounter(x => x * 2)
: _ => setCounter(x => x + 1)
, turbo
? Math.floor(delay / 2)
: delay
)

const toggleTurbo = () =>
setTurbo(t => !t)

const toggleDoubler = () =>
setDoubler(t => !t)

return <span>
{counter}
<button
onClick={start}
disabled={busy}
children="Start"
/>
<button
onClick={toggleDoubler}
disabled={!busy}
children={`Doubler: ${doubler ? "ON" : "OFF"}`}
/>
<button
onClick={toggleTurbo}
disabled={!busy}
children={`Turbo: ${turbo ? "ON" : "OFF"}`}
/>
<button
onClick={stop}
disabled={!busy}
children="Stop"
/>
</span>
}

function Main ()
{ const [timers, setTimers] = useState([])

const addTimer = () =>
setTimers(r => append(r, <MyTimer />))

const destroyTimer = c => () =>
setTimers(r => remove(r, c))

return <main>
<p>Run in expanded mode. Open your developer console</p>
<button
onClick={addTimer}
children="Add Timer"
/>
{ timers.map((c, key) =>
<div key={key}>
{c}
<button
onClick={destroyTimer(c)}
children="Destroy"
/>
</div>
)}
</main>
}

ReactDOM.render
( <Main/>
, document.getElementById("react")
)
<div id="react"></div>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/16.13.1/umd/react.production.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/16.13.1/umd/react-dom.production.min.js"></script></script>

关于reactjs - 使用 setTimeout 延迟在 useEffect 中 react setInterval,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/61971791/

24 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com