使用React Hooks卸载组件时如何访问状态



使用常规React,可能会有这样的东西:

class NoteEditor extends React.PureComponent {
constructor() {
super();
this.state = {
noteId: 123,
};
}
componentWillUnmount() {
logger('This note has been closed: ' + this.state.noteId);
}
// ... more code to load and save note
}

在React Hooks中,可以这样写:

function NoteEditor {
const [noteId, setNoteId] = useState(123);
useEffect(() => {
return () => {
logger('This note has been closed: ' + noteId); // bug!!
}
}, [])
return '...';
}

useEffect返回的内容将在组件卸载之前只执行一次,但是状态(如上面的代码中所示(将是过时的。

解决方案是将noteId作为依赖项传递,但效果将在每次渲染中运行,而不仅仅是一次。或者使用引用,但这很难维护。

那么,有什么推荐的模式可以使用React Hook来实现这一点吗?

使用常规React,可以从组件中的任何位置访问状态,但使用钩子,似乎只有复杂的方法,每种方法都有严重的缺点,或者我只是缺少了一些东西。

有什么建议吗?

useRef((救援。

由于ref是可变的,并且在组件的生命周期内存在,因此无论何时更新,我们都可以使用它来存储当前值,并且仍然可以通过ref的值.current属性在useEffect的清理函数中访问该值。

因此,将有一个额外的useEffect((来在状态发生变化时保持ref值的更新。

示例片段

const [value, setValue] = useState();
const valueRef = useRef();
useEffect(() => {
valueRef.current = value;
}, [value]);
useEffect(() => {
return function cleanup() {
console.log(valueRef.current);
};
}, []);

感谢的作者https://www.timveletta.com/blog/2020-07-14-accessing-react-state-in-your-component-cleanup-with-hooks/.请参阅此链接了解深海潜水。

useState()useReducer()的一种特殊形式,因此您可以替换一个完整的减缩器来获得当前状态并绕过闭包问题。

NoteEditor

import React, { useEffect, useReducer } from "react";
function reducer(state, action) {
switch (action.type) {
case "set":
return action.payload;
case "unMount":
console.log("This note has been closed: " + state); // This note has been closed: 201
break;
default:
throw new Error();
}
}
function NoteEditor({ initialNoteId }) {
const [noteId, dispatch] = useReducer(reducer, initialNoteId);
useEffect(function logBeforeUnMount() {
return () => dispatch({ type: "unMount" });
}, []);
useEffect(function changeIdSideEffect() {
setTimeout(() => {
dispatch({ type: "set", payload: noteId + 1 });
}, 1000);
}, []);
return <div>{noteId}</div>;
}
export default NoteEditor;

应用程序

import React, { useState, useEffect } from "react";
import "./styles.css";
import NoteEditor from "./note-editor";
export default function App() {
const [notes, setNotes] = useState([100, 200, 300]);
useEffect(function removeNote() {
setTimeout(() => {
setNotes([100, 300]);
}, 2000);
}, []);
return (
<div className="App">
<h1>Hello CodeSandbox</h1>
<h2>Start editing to see some magic happen!</h2>
{notes.map(note => (
<NoteEditor key={`Note${note}`} initialNoteId={note} />
))}
</div>
);
}

我想插话回答这个问题,以防其他人遇到这个问题。如果在useEffect卸载函数中需要多个值,那么确保使用正确的依赖关系非常重要。因此,接受的答案很好,因为它只是一个依赖项,但开始包含更多的依赖项,这变得很复杂。你需要的useRef的数量失控了。因此,您可以使用useRef作为卸载函数本身,并在卸载组件时调用它:

import React, { useRef, useState, useContext, useCallback, useEffect } from 'react';
import { Heading, Input } from '../components';
import { AppContext } from '../../AppContext';
export const TitleSection: React.FC = ({ thing }) => {
const { updateThing } = useContext(AppContext);
const [name, setName] = useState(thing.name);
const timer = useRef(null);
const onUnmount = useRef();
const handleChangeName = useCallback((event) => {
setName(event.target.value);
timer.current !== null && clearTimeout(timer.current);
timer.current = setTimeout(() => {
updateThing({
name: name || ''
});
timer.current = null;
}, 1000);
}, [name, updateThing]);
useEffect(() => {
onUnmount.current = () => {
if (thing?.name !== name) {
timer.current !== null && clearTimeout(timer.current);
updateThing({
name: name || '',
});
timer.current = null;
}
};
}, [thing?.name, name, updateThing]);
useEffect(() => {
return () => {
onUnmount.current?.();
};
}, []);
return (
<>
<Heading as="h1" fontSize="md" style={{ marginBottom: 5 }}>
Name
</Heading>
<Input
placeholder='Grab eggs from the store...'
value={name}
onChange={handleChangeName}
variant='white'
/>
</>
);
};

对于ref中多个项的用例,您可以将ref作为一个对象,并向其添加您想要的任何键值:

const [value1, setValue1] = useState();
const [value2, setValue2] = useState();
const valueRef = useRef();
useEffect(() => {
valueRef.current = { value1, value2 };
}, [value1, value2]);
useEffect(() => {
return function cleanup() {
console.log(`retained both ${valueRef.current.value1} and ${valueRef.current.value2}`);
};
}, []);

相关内容

  • 没有找到相关文章

最新更新