我有一个 nextjs 应用程序。有一个页面包含文档列表。如果有人上传文档,列表中就会添加一项。任务是,如果有人移出页面,并且列表中仍然有一些项目,我们需要为每个文档调用一个 API。在react和nextjs中怎么能做到呢?
export const Page = () => {
const [documents, setDocuments] = useState([]);
const deleteDocuments = useCallback(() => {
documents.forEach(d => deleteFunction)
}, [deleteFunction, documents]);
useEffect(() => {
return () => deleteDocuments()
}, [deleteDocuments] );
return "...some..html.."
}
我们正在使用
useEffect
的清理功能来处理这个问题。
但是,问题是,由于使用了删除函数,因此它是
useEffect
依赖项的一部分,并且由于该函数依赖于 documents
,因此它是依赖项函数的一部分。因此,每当用户在文档数组中添加文档时,都会调用 useEffect 的清理函数,因为数组发生了更改。
我希望这个函数只在页面离开时调用一次。 (如果最初在反应严格模式下被调用两次也没关系)
我们可以通过两种方法来修复
第一
我们可以省略删除函数的依赖
const deleteDocuments = useCallback(() => {
documents.forEach(d => deleteFunction)
}, [deleteFunction]); // <-- remove documents from the list
useEffect(() => {
return () => deleteDocuments()
}, [deleteDocuments] );
我不喜欢这种方法,因为我们永远不知道这个东西什么时候会因为未处理的依赖而开始失败
第二个
使用
useRef
const [documents, setDocuments] = useState([]);
const documentsRef = useRef(documents);
useEffect(() => {
documentsRef.current = documents;
}, [documents]);
const deleteDocuments = useCallback(() => {
documentsRef.current.forEach(d => deleteFunction)
}, [deleteFunction]); // <- no need to have documents or documentRef as dependency
useEffect(() => {
return () => deleteDocuments()
}, [deleteDocuments] );
如果我们维护
documentRef
,我们就不需要在依赖数组中维护它。
但是,我不确定这是否是最好的方法。因为 React 建议不要陷入使用 useRef 的陷阱。然而,他们建议的示例对他们来说是有意义的,因为它会连接并关闭连接,但我们的示例不容易出现该错误,因为一旦呈现某些内容,文档数组将为 [] 并且删除文档并不重要对于空数组,函数被调用两次。
还有更好的吗?
有更好的解决方案来解决这个问题吗?
PS
我明白了,我还没有添加window.beforeunload
事件。为了简单起见,我在示例中省略了这一点,但我计划将其用于实际的生产代码中。
React.StrictMode
组件效果。您只需要一个
useEffect
钩子调用即可返回一个清理函数,该函数在每个已安装组件的生命周期中准确地运行一次。 我认为,使用
useRef
钩子就足够了。我唯一的建议是稍微收紧代码。无需记住外部回调来删除文档,只需直接迭代 documentsRef.current
数组即可。
示例:
const [documents, setDocuments] = useState([]);
const documentsRef = useRef(documents);
useEffect(() => {
documentsRef.current = documents;
}, [documents]);
useEffect(() => {
return () => {
documentsRef.current.forEach(deleteFunction);
};
}, []);
为了完整起见,这里有一个
beforeunload
事件监听器的示例:
const [documents, setDocuments] = useState([]);
const documentsRef = useRef(documents);
useEffect(() => {
documentsRef.current = documents;
}, [documents]);
useEffect(() => {
const deleteDocuments = () => {
documentsRef.current.forEach(deleteFunction);
};
// handles when page is unloaded
window.addEventListener("beforeunload", deleteDocuments);
// cleanup function handles when component unmounts
return () => {
window.removeEventListener("beforeunload", deleteDocuments);
deleteDocuments();
};
}, []);