[英]useEffect hook misbehaves with setTimeout and state
I created a custom toast component in my exercise React application.我在练习 React 应用程序中创建了一个自定义 Toast 组件。 It is working correctly until the moment I try to introduce an auto dismiss timeout functionality.
在我尝试引入自动关闭超时功能之前,它一直工作正常。 Basically when you load a new toast it needs to dismiss itself after let say 5000ms.
基本上,当你加载一个新的 toast 时,它需要在 5000 毫秒后自行关闭。
Easiest way to create toast is put invalid mail / password.创建 toast 的最简单方法是放置无效的邮件/密码。
I believe I am doing something wrong with the useEffect
hook or I am missing something.我相信我在
useEffect
钩子上做错了什么,或者我遗漏了一些东西。 The problem is that when I am creating multiple toasts they disappear all at the same time.问题是,当我创建多个 toast 时,它们会同时消失。 Also React is complaining that I didn't include
remove
as a dependency of the useEffect
hook but when I do it becomes even worse. React 还抱怨我没有将
remove
作为useEffect
钩子的依赖项包含useEffect
但是当我这样做时,情况变得更糟。 Can someone demystify why this is happening and how it can be fixed.有人可以揭开为什么会发生这种情况以及如何解决它的神秘面纱。 I am a bit new to React.
我对 React 有点陌生。
Here is the file that creates a HOC around my main App component:这是围绕我的主要 App 组件创建 HOC 的文件:
import React, { useState } from 'react';
import { createPortal } from 'react-dom';
import ToastContext from './context';
import Toast from './Toast';
import styles from './styles.module.css';
function generateUEID() {
let first = (Math.random() * 46656) | 0;
let second = (Math.random() * 46656) | 0;
first = ('000' + first.toString(36)).slice(-3);
second = ('000' + second.toString(36)).slice(-3);
return first + second;
}
function withToastProvider(Component) {
function WithToastProvider(props) {
const [toasts, setToasts] = useState([]);
const add = (content, type = 'success') => {
const id = generateUEID();
if (toasts.length > 4) {
toasts.shift();
}
setToasts([...toasts, { id, content, type }]);
};
const remove = id => {
setToasts(toasts.filter(t => t.id !== id));
};
return (
<ToastContext.Provider value={{ add, remove, toasts }}>
<Component {...props} />
{ createPortal(
<div className={styles.toastsContainer}>
{ toasts.map(t => (
<Toast key={t.id} remove={() => remove(t.id)} type={t.type}>
{t.content}
</Toast>
)) }
</div>,
document.body
) }
</ToastContext.Provider>
);
}
return WithToastProvider;
}
export default withToastProvider;
And the Toast component:和 Toast 组件:
import React, { useEffect } from 'react';
import styles from './styles.module.css';
function Toast({ children, remove, type }) {
useEffect(() => {
const duration = 5000;
const id = setTimeout(() => remove(), duration);
console.log(id);
return () => clearTimeout(id);
}, []);
return (
<div onClick={remove} className={styles[`${type}Toast`]}>
<div className={styles.text}>
<strong className={styles[type]}>{type === 'error' ? '[Error] ' : '[Success] '}</strong>
{ children }
</div>
<div>
<button className={styles.closeButton}>x</button>
</div>
</div>
);
}
export default Toast;
Searching today for the solution I found it here今天搜索我在这里找到的解决方案
You will need to use useRef and its current
property您将需要使用useRef及其
current
属性
Here is how I transformed the Toast
component to work:以下是我如何将
Toast
组件转换为工作:
import React, { useEffect, useRef } from 'react';
import styles from './styles.module.css';
function Toast({ children, remove, type }) {
const animationProps = useSpring({opacity: .9, from: {opacity: 0}});
const removeRef = useRef(remove);
removeRef.current = remove;
useEffect(() => {
const duration = 5000;
const id = setTimeout(() => removeRef.current(), duration);
return () => clearTimeout(id);
}, []);
return (
<div onClick={remove} className={styles[`${type}Toast`]}>
<div className={styles.text}>
<strong className={styles[type]}>{type === 'error' ? '[Error] ' : '[Success] '}</strong>
{ children }
</div>
<div>
<button className={styles.closeButton}>x</button>
</div>
</div>
);
}
export default Toast;
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.