我有一个支付页面,用户点击按钮&;pay &;,然后一个模式打开,它显示加载…,则它要么显示"付款成功";组件或"付款失败";组件。
当它呈现两者中的任何一个时,它会播放声音效果,
- 成功后,播放CheckTennnn的音效!✅"付款成功"
- 失败时,它会播放EEeerr..❌"付款失败"。
我实现它的方式是通过一个useCallback来决定在模态中渲染哪个视图,[成功或失败视图]。
export default ProcessPaymentModal({ isSuccess, isError, isLoading }){
const [timer, setTimer] = useState(0)
useEffect(()=> {
const intervalId = setInterval(()=> setTimer((previousState)=> previousState + 1), 1000)
return ()=> clearInterval(intervalId)
})
const View = useCallback(()=>{
switch(true){
case isSuccess:
return <PaymentSuccessView timer={timer}/>
case isError:
return <PaymentFailView timer={timer}/>
case isLoading:
return <LoadingView />
}
}, [timer, isSuccess, isError, isLoading])
return (
<React.Fragment>
some content
<View />
</React.Fragment>
)
}
在这些失败或成功组件中,我有一个useEffect,它只在挂载时播放一次音频(并且它必须只播放一次声音)。
export default function PaymentSuccessView({ timer }) {
useEffect(() => {
const soundEffect = new Audio('../media/checktennn.mp3')
soundEffect.play()
}, []);
return <button> OK ({timer}) </button>;
}
这是一个堆栈闪电战实例,一个代码沙箱测试,(点击我)
然而,问题是每次计时器改变时它都会继续播放声音,
- CheckTennnn !✅
- CheckTennnn !✅
- CheckTennnn !✅
就像这样,你知道的。
即使我在<PaymentSuccessView />
组件中有一个空的依赖数组,这意味着这个useEffect
函数必须只运行一次。山上。
这里的问题是您使用useCallback
来定义组件。只要它的值依赖于当前的timer
,每次timer
发生变化,View
组件的值就会发生变化,React DOM就会附加新的新类型的元素。因此,在新的View
中使用的每个组件都将被安装到console.log
中。
要解决这个问题,应该单独定义组件View
,以免每次timer
改变时都改变它的值,或者重构modal代码,如下所示:
export default ProcessPaymentModal({ isSuccess, isError, isLoading }){
const [timer, setTimer] = useState(0)
useEffect(()=> {
const intervalId = setInterval(()=> setTimer((previousState)=> previousState + 1), 1000)
return ()=> clearInterval(intervalId)
})
if (isSuccess) {
return <PaymentSuccessView timer={timer}/>
}
if (isError) {
return <PaymentFailView timer={timer}/>
}
return <LoadingView />;
}
也,固定stackblitz的例子来尝试:https://stackblitz.com/edit/react-f8atrq?file=src/components/Parent.jsx
只要使用<Strict>
组件,您将看到两倍的控制台消息
我找到了另一个解决方案,使用useCallback
,
你可以直接将props传递给<View />
组件,如下所示:
export default ProcessPaymentModal({ isSuccess, isError, isLoading }){
const [timer, setTimer] = useState(0)
useEffect(()=> {
const intervalId = setInterval(()=> setTimer((previousState)=> previousState + 1), 1000)
return ()=> clearInterval(intervalId)
})
const View = useCallback(({ timer })=>{
switch(true){
case isSuccess:
return <PaymentSuccessView timer={timer} />
case isError:
return <PaymentFailView timer={timer} />
case isLoading:
return <LoadingView />
}
}, [isSuccess, isError, isLoading]) // no need to have timer in the array anymore
return (
<React.Fragment>
some content
<View timer={timer} />
</React.Fragment>
)
}
分叉堆栈闪电战的例子,一个沙盒,(点击我)