在useCallback中渲染的组件中的 useEffect空依赖数组没有意义



我有一个支付页面,用户点击按钮&;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>
)
}

分叉堆栈闪电战的例子,一个沙盒,(点击我)

相关内容

最新更新