如何记住一个 React hooks 组件被卸载,从而避免状态变化?
How remember that a React hooks component is unmounted, so can avoid state changes?
使用基于 class 的旧 React.js 代码,我可以这样做,以记住组件已被卸载:
componentWillUnmount: function() {
this.isGone = true;
},
loadUsers: function() {
Server.loadUsers(..., (response) => {
if (this.isGone) return;
...
});
}
如何使用基于钩子的组件做同样的事情?
这是一个示例挂钩函数组件,我不确定如何记住它已被卸载,所以我可以 return
在调用任何 setSomeState
:
之前
export const GroupMembers = React.createFactory(function(props) {
const [membersOrNull, setMembers] = React.useState(null);
let isGone = false; // ? (se [222] below
React.useEffect(() => {
Server.listGroupMembers(someGroupId, (response) => {
if (/* is unmounted ?? */) return;
setMembers(response.members);
});
return () => { /* how remember is unmounted?
would isGone = true; work? */ };
}, []);
...
return ..., Button({ title: "Remove all members", onClick: () => {
Server.removeAllMembers(someGroupId, () => {
if (/* is unmounted ?? */) return;
setMembers([]);
});
}});
我想我不能使用 const [isGone, setGone] = useState(false)
因为我不应该在卸载后尝试访问状态(读取 isGone
)。而如果我 [222] 在函数内部添加一个本地 let isGone = false
,在我看来,在函数内部创建的各种回调,将引用不同的 "instances"这个局部变量,取决于创建不同回调的不同 GroupMembers(..)
调用?还是我错了,这行得通吗? — 也许我可以创建一个带有局部 let isGone = false
的外部包装函数,但是,这会添加另一个包装函数和缩进:- /
只使用通过闭包访问的局部变量
React.useEffect(() => {
let isActual = true;
Server.listGroupMembers(someGroupId, (response) => {
if (!isActual) return;
setMembers(response.members);
});
return () => {isActual = false;};
}, []);
在这种情况下,标志只会在卸载时更新。但在一般情况下(useEffect
有一些非空依赖项)它也会使用。因此,在顺序渲染的情况下,您可以确定您永远不会处理较旧的请求。
PS最通用的方法是尽可能取消请求。
由于您也在 useEffect
方法之外使用 isGone
标志,因此您可以使用 useRef
来存储像
这样的变量
export const GroupMembers = React.createFactory(function(props) {
const [membersOrNull, setMembers] = React.useState(null);
const isGone = useRef(false); // ? (se [222] below
React.useEffect(() => {
Server.listGroupMembers(someGroupId, (response) => {
if (isGone.current) return;
setMembers(response.members);
});
return () => {
isGone.current = true;
};
}, []);
...
return ..., Button({ title: "Remove all members", onClick: () => {
Server.removeAllMembers(someGroupId, () => {
if (isGone.current) return;
setMembers([]);
});
}});
PS: A better way to handle such things is to cancel the request when
you are leaving the page instead of waiting for the response only to
neglect it.
使用基于 class 的旧 React.js 代码,我可以这样做,以记住组件已被卸载:
componentWillUnmount: function() {
this.isGone = true;
},
loadUsers: function() {
Server.loadUsers(..., (response) => {
if (this.isGone) return;
...
});
}
如何使用基于钩子的组件做同样的事情?
这是一个示例挂钩函数组件,我不确定如何记住它已被卸载,所以我可以 return
在调用任何 setSomeState
:
export const GroupMembers = React.createFactory(function(props) {
const [membersOrNull, setMembers] = React.useState(null);
let isGone = false; // ? (se [222] below
React.useEffect(() => {
Server.listGroupMembers(someGroupId, (response) => {
if (/* is unmounted ?? */) return;
setMembers(response.members);
});
return () => { /* how remember is unmounted?
would isGone = true; work? */ };
}, []);
...
return ..., Button({ title: "Remove all members", onClick: () => {
Server.removeAllMembers(someGroupId, () => {
if (/* is unmounted ?? */) return;
setMembers([]);
});
}});
我想我不能使用 const [isGone, setGone] = useState(false)
因为我不应该在卸载后尝试访问状态(读取 isGone
)。而如果我 [222] 在函数内部添加一个本地 let isGone = false
,在我看来,在函数内部创建的各种回调,将引用不同的 "instances"这个局部变量,取决于创建不同回调的不同 GroupMembers(..)
调用?还是我错了,这行得通吗? — 也许我可以创建一个带有局部 let isGone = false
的外部包装函数,但是,这会添加另一个包装函数和缩进:- /
只使用通过闭包访问的局部变量
React.useEffect(() => {
let isActual = true;
Server.listGroupMembers(someGroupId, (response) => {
if (!isActual) return;
setMembers(response.members);
});
return () => {isActual = false;};
}, []);
在这种情况下,标志只会在卸载时更新。但在一般情况下(useEffect
有一些非空依赖项)它也会使用。因此,在顺序渲染的情况下,您可以确定您永远不会处理较旧的请求。
PS最通用的方法是尽可能取消请求。
由于您也在 useEffect
方法之外使用 isGone
标志,因此您可以使用 useRef
来存储像
export const GroupMembers = React.createFactory(function(props) {
const [membersOrNull, setMembers] = React.useState(null);
const isGone = useRef(false); // ? (se [222] below
React.useEffect(() => {
Server.listGroupMembers(someGroupId, (response) => {
if (isGone.current) return;
setMembers(response.members);
});
return () => {
isGone.current = true;
};
}, []);
...
return ..., Button({ title: "Remove all members", onClick: () => {
Server.removeAllMembers(someGroupId, () => {
if (isGone.current) return;
setMembers([]);
});
}});
PS: A better way to handle such things is to cancel the request when you are leaving the page instead of waiting for the response only to neglect it.