我构建了一个“中继器”组件,该组件接受要重复作为道具的组件。有添加,删除和清除功能可添加或删除实例,或清除所有实例。
我正在尝试使用此转发器组件来呈现另一个具有setTimeout的组件。如果添加或删除其他组件,则所有其他组件的setTimouts都会重置,因此它们都在同一时间触发。
我正在使用的转发器组件:
class Repeater extends React.Component {
state = {
elements: [],
};
component = Notification; // For simplicity - actually dynamic from props
count = 0;
getKey = () => {
return this.count++;
};
add = props => {
const { elements } = this.state;
props.key = this.getKey();
elements.push(props);
this.setState({ elements });
};
remove = key => {
const { elements } = this.state;
const newElements = elements.filter(element => {
return element.key !== key;
});
this.setState({ elements: newElements });
};
clear = () => {
this.setState({ elements: [] });
};
render() {
const { elements } = this.state;
const Component = this.component;
return (
<React.Fragment>
{elements.map(element => {
return <Component key={element.key} {...element} />;
})}
</React.Fragment>
);
}
}
我正在渲染的组件:
export function Notification() {
const {
isOpen = true,
message,
title,
duration = 4.5,
} = props;
const [openState, setOpenState] = React.useState(isOpen);
let timer;
const clearTimer = () => {
window.clearTimeout(timer);
};
const handleClose = () => {
clearTimer();
console.log('Test');
};
const setTimer = () => {
if (duration) timer = setTimeout(() => handleClose(), duration * 1000);
};
if (openState) {
setTimer();
return (
<div
onMouseEnter={() => clearTimer()}
onMouseLeave={() => setTimer()}>
<div>
<Icon />
</div>
<div>{title}</div>
<div>{message}</div>
</div>
);
}
return null;
}
答案 0 :(得分:0)
在这种情况下,使用.map函数是一个问题。您需要分别渲染每个组件,否则映射函数将在每次触发更新时运行,并将元素重置为其初始状态。
我更新了解决方案以使用最新的ES6 +功能。您想要实现的功能和代码的完整上下文仍然有待改进的地方,但这是第一个应该起作用的迭代,因为setState函数引用了先前的状态。
import React, { useState, useEffect } from 'react';
export function Notification({ duration=4500, isOpen=true, message, title }) {
const [open, setOpen] = useState(isOpen);
let timer;
useEffect(() => {
setTimer();
});
const handleClose = () => {
clearTimer();
};
const clearTimer = () => {
window.clearTimeout(timer);
};
const setTimer = () => {
timer = setTimeout(() => handleClose(), duration);
};
if (!open) return null;
return (
<div
onMouseEnter={() => clearTimer()}
onMouseLeave={() => setTimer()}>
<div>
<Icon />
</div>
<div>{title}</div>
<div>{message}</div>
</div>
);
};
export function Repeater({ component }) {
const [elements, setElements] = useState([]);
const [count, setCount] = useState(0);
const Component = component ? component : Notification;
const getKey = () => {
setCount(count => count + 1);
return count;
};
const add = (props) => {
setElements((elements) => elements.push({ ...props, key: getKey }));
};
const remove = (key) => {
setElements((elements) => elements.filter((e) => e.key !== key));
};
const clear = () => {
setElements([]);
};
const renderedElements = elements.map(({ key, ...rest }) => (
<Component key={key} {...rest} />
));
return (
<>
{renderedElements}
</>
);
};
答案 1 :(得分:0)
我能够通过以下方式解决此问题:在第一次加载时将通知组件的呈现时间存储为状态,然后在setTimer方法中找到它与当前时间之间的差异。
...
const [renderTime] = React.useState(Date.now());
...
const setTimer = () => {
if (duration) {
const elapsed = Date.now() - openTime;
const timeout = duration - elapsed;
timer = setTimeout(() => handleClose(), timeout);
}
};