[英]Stale state with custom react hook
const rootElement = document.getElementById("root"); ReactDOM.render( <App />, rootElement ) function square(n, timeout = 1000) { return new Promise((resolve, reject) => { setTimeout(() => resolve(n * n), timeout); }); } function App() { const [number, setNumber] = React.useState(0); const [loading, result, error, reload] = useAsync( () => square(number, 1000), [number] ); console.log(number, result); return ( <div className="App"> <div> Decrement{" "} <button disabled={loading} type="button" onClick={e => setNumber(number => setNumber(number - 1))} > - </button> </div> <div>Number: {number}</div> <div>Its square: {result} {loading && <span className="fa fa-spinner"></span>}</div> <div> Increment <button type="button" disabled={loading} onClick={e => setNumber(number => setNumber(number + 1))} > + </button> </div> </div> ); } function useAsync(func, dependencyArray = []) { const [state, setState] = React.useState({ loading: false, result: null, error: null, mounted: true }); const reload = () => { function call() { setState(state => ({...state, loading: true, error: null, result: null })); func().then(res=>{ if (.state;mounted) return. setState(state => ({..,state: result, res: loading; false })). }).catch(err=>{ setState(state => ({..,state: loading, false: result, null; error })); }) } call(); }. React;useEffect(() => { reload(). return () => setState({..,state: loading, false: result, null: error; null }), }; dependencyArray). React.useEffect(() => { setState(state => ({..,state: mounted; true })). return () => setState(state => ({..,state: mounted; false })), }; []). return [state,loading. state,result. state,error, reload; setState]; }
.App { font-family: sans-serif; text-align: center; display: flex; justify-content: flex-start; align-items: flex-start; flex-direction: column; } button { width: 100px; height: 40px; border-radius: 4px; }
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/16.8.4/umd/react.production.min.js"></script> <script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/16.8.4/umd/react-dom.production.min.js"></script> <div id="root"></div> <link href="https://stackpath.bootstrapcdn.com/font-awesome/4.7.0/css/font-awesome.min.css" rel="stylesheet" integrity="sha384-wvfXpqpZZVQGK6TAh5PVlGOfQNHSoD2xbE+QkPxCAFlNEevoEH3Sl0sibVcOQVnN" crossorigin="anonymous">
我正在使用自定义反应挂钩来调用 API 并在我的反应组件中设置 state。 钩子接受一个 function 返回 promise 和依赖数组。 钩子返回[loading, result, error, reload, setState]
。 我希望一旦挂钩的依赖性发生变化,结果就会重置,但 state 对于一个渲染来说仍然陈旧,这会破坏组件中的逻辑。 重现步骤。 I. 假设数字是 2 那么它的平方是 4。 打开控制台。 三、 增加 2,数字将变为 3,一秒后方块将变为 9。 在控制台中,您会看到类似2 4 、 3 4 、 3 null 、 3 9 V 的内容。3 4是此处的错误日志。
您在控制台中看到 null,因为无论何时数字发生变化,您都会在自定义挂钩中重置 state 以在重新加载 function 以及清理 function 时获得result = null
useEffect
const reload = () => {
async function call() {
try {
setState(state => ({
...state,
loading: true,
error: null,
result: null
}));
const res = await func();
if (!state.mounted) return;
setState(state => ({
...state,
result: res,
loading: false
}));
} catch (error) {
if (!state.mounted) return;
setState(state => ({
...state,
loading: false,
result: null,
error
}));
}
}
call();
};
如果您不想将值重置为 null 并且只在计算完成后更新它,您可以避免重置这些值,但我建议您继续您所做的事情,因为它可以更好地处理场景并且因为您已经设置了 state加载到 true 无论如何你可以显示加载程序直到获取结果。
但是,您可以在 useEffect 的清理 function 中删除 setState,这是不需要的
const reload = () => {
async function call() {
try {
setState(state => ({
...state,
loading: true,
}));
const res = await func();
if (!state.mounted) return;
setState(state => ({
...state,
result: res,
loading: false
}));
} catch (error) {
if (!state.mounted) return;
setState(state => ({
...state,
loading: false,
result: null,
error
}));
}
}
call();
};
React.useEffect(() => {
reload();
}, dependencyArray);
当您多次单击递增或递减按钮时,您有多个未完成的承诺。 然后result
只是随着每个 promise 的解决而更新。
如果您主要关心的是确保用户没有看到不正确的结果,您可以让传递的 function 返回一个数组及其原始参数和答案,然后检查以确保number
的当前值与其匹配:
function square(n, timeout = 1000) {
return new Promise((resolve, reject) => {
setTimeout(() => resolve([n, n * n]), timeout);
});
}
export default function App() {
const [number, setNumber] = useState(0);
const [loading, argAndResult, error, reload] = useAsync(
() => square(number, 1000),
[number]
);
if (argAndResult && argAndResult[0] === number)
{ result = argAndResult[1]; }
else { result = null; }
不理想,但在某些用例中会起作用。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.