我的橡皮鸭没有给我答案。他通常更聪明。
在我的Create-React-App中,我有一个警报上下文和一个react组件,用于处理整个应用中的弹出警报。我是第一次玩React Hooks,并尝试在每个Alert上设置一个倒数计时器。
我编写了一个useCountdown钩子(如下所示的代码),该钩子在其他测试组件上也可以正常工作,但是当我尝试将其与警报一起使用时,给定会话中的每个警报都使用触发的第一个警报中的计时器值。
在此错误警报的屏幕快照中,显示的每个警报具有非常不同的倒计时值,但是它们都显示了第一个警报的倒计时值。倒数计时在右上角。
我已经使用过useEffect()来尝试解决此问题,但很明显,我还有很多关于钩子及其作用域的知识要学习。
任何建议将不胜感激。最终,我将计时器设置为一个小的手动关闭按钮,并带有秒数,直到自动关闭显示为按钮的文本。现在...只是在丑陋的模式。
useCountdown挂钩
import { useState, useEffect } from 'react'
const useCountdown = (m = 1, s = 10) => {
const [minutes, setMinutes] = useState(m)
const [seconds, setSeconds] = useState(s)
useEffect(() => {
let myInterval = setInterval(() => {
if (seconds > 0) {
setSeconds(seconds - 1)
}
if (seconds === 0) {
if (minutes === 0) {
clearInterval(myInterval)
} else {
setMinutes(minutes - 1)
setSeconds(59)
}
}
}, 1000)
return () => {
clearInterval(myInterval)
}
})
const finalSeconds = seconds < 10 ? `0${seconds}` : `${seconds}`
return `${minutes}:${finalSeconds}`
}
export default useCountdown
警报组件
import React, { useContext, Fragment } from 'react'
import AlertContext from '../../context/alert/alertContext'
import { CSSTransition, TransitionGroup } from 'react-transition-group'
import styled from 'styled-components' // TODO Refactor to styled
// Custom hook to give each Alert a countdown timer
import useCountdown from '../../hooks/useCountdown' // BUG!
const Alerts = () => {
// const testAlert = {
// id: 12345,
// title: 'Problem',
// color: 'alert-danger',
// icon: 'fas fa-exclamation-triangle',
// seconds: 20,
// msg: ['This is an alert. Testing 123.', 'Auto-dismiss in 20 seconds.'],
// }
const alertContext = useContext(AlertContext)
const { removeAlert } = alertContext
// ISSUE #32 The initial value gets used through-out session
const countdown = useCountdown(alert.seconds)
return (
<Fragment>
<TransitionGroup>
{alertContext.alerts.length > 0 &&
alertContext.alerts.map((alert) => (
<CSSTransition key={alert.id} timeout={500} classNames='pop'>
<div className={`alert ${alert.color}`}>
<div className='alert-title'>
<h1 onClick={() => removeAlert(alert.id)}>
<i className={`${alert.icon}`} /> <span className='hide-sm'>{alert.title}</span>
</h1>
<span className='alert-countdown'>{countdown}</span>
<button className='btn btn-link btn-sm alert-btn hide-sm' onClick={() => removeAlert(alert.id)}>
<i className='far fa-times-square fa-3x' />
</button>
</div>
<div className='alert-items'>
<ul>
{alert.msg.map((item) => (
<li key={item}>
<i className='fas fa-chevron-circle-right'></i> <span>{item}</span>
</li>
))}
</ul>
</div>
</div>
</CSSTransition>
))}
</TransitionGroup>
</Fragment>
)
}
export default Alerts
问题是您useCountdown
在Alerts
容器组件中使用了自己,因此为所有子元素存储了一个状态。基本上,当前您只有一个时间间隔,因此有一个用于所有警报的倒计时值。
如果希望每个警报具有单独的状态(倒计时值),则应将警报UI提取到单独的组件中并在其中使用useCountdown
钩子。然后,重构Alerts容器Alert
组件以为每个实例呈现一个组件。
这是示例代码。它可能包含语法错误,因为我现在无法测试,但可以尝试:
警报容器组件:
import React, { useContext, Fragment } from 'react'
import AlertContext from '../../context/alert/alertContext'
import { CSSTransition, TransitionGroup } from 'react-transition-group'
import styled from 'styled-components' // TODO Refactor to styled
const Alerts = () => {
const alertContext = useContext(AlertContext)
const { removeAlert } = alertContext
return (
<Fragment>
<TransitionGroup>
{alertContext.alerts.length > 0 &&
alertContext.alerts.map((alertInstance) => (
<CSSTransition key={alertInstance.id} timeout={500} classNames='pop'>
<Alert alert={alertInstance} removeAlert={removeAlert}/>
</CSSTransition>
))}
</TransitionGroup>
</Fragment>
)
}
export default Alerts
警报组件:
export function Alert(props) {
const countdown = useCountdown(props.alert.seconds);
return (
<div className={`alert ${props.alert.color}`}>
<div className='alert-title'>
<h1 onClick={() => props.removeAlert(props.alert.id)}>
<i className={`${props.alert.icon}`} /> <span className='hide-sm'>{props.alert.title}</span>
</h1>
<span className='alert-countdown'>{countdown}</span>
<button className='btn btn-link btn-sm alert-btn hide-sm' onClick={() => props.removeAlert(props.alert.id)}>
<i className='far fa-times-square fa-3x' />
</button>
</div>
<div className='alert-items'>
<ul>
{props.alert.msg.map((item) => (
<li key={item}>
<i className='fas fa-chevron-circle-right'></i> <span>{item}</span>
</li>
))}
</ul>
</div>
</div>
);
}
另外,在使用中更改依赖于先前值的状态时,setMinutes(minutes - 1)
使用更新程序功能,而不是简单地提供一个值。在这种情况下,请确保您的更新将是准确的,并且不会对过时的数据进行更新,从而最终导致错误状态。您可以在此处了解更多信息:useState参考。
因此,您的钩子应如下所示:
const useCountdown = (m = 1, s = 10) => {
const [minutes, setMinutes] = useState(m)
const [seconds, setSeconds] = useState(s)
useEffect(() => {
let myInterval = setInterval(() => {
if (seconds > 0) {
setSeconds((seconds) => seconds - 1)
}
if (seconds === 0) {
if (minutes === 0) {
clearInterval(myInterval)
} else {
setMinutes((minutes) => minutes - 1)
setSeconds(59)
}
}
}, 1000)
return () => {
clearInterval(myInterval)
}
})
const finalSeconds = seconds < 10 ? `0${seconds}` : `${seconds}`
return `${minutes}:${finalSeconds}`
}
本文收集自互联网,转载请注明来源。
如有侵权,请联系 [email protected] 删除。
我来说两句