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 并在我的反应组件中设置状态。 hook 接受一个返回承诺和依赖数组的函数。钩子返回[loading, result, error, reload, setState]
. 我希望一旦钩子的依赖性发生变化,结果就会重置,但对于一个破坏组件逻辑的渲染来说,状态仍然过时。 重现步骤。 I. 假设数是 2,那么它的平方是 4。 二、打开控制台。 III.递增2,数变为3,一秒后平方变为9。 IV. 在控制台中,您将看到类似24、3 4、3 null、3 9 V.3 4是这里的错误日志。
您在控制台中看到 null,因为当数字发生变化时,您正在重置自定义钩子中的状态,以便在重新加载函数以及 useEffect 的清理函数中result = null
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 并且仅在计算完成后更新它,您可以避免重置值,但我建议您继续您所做的工作,因为它可以更好地处理场景,并且由于您已将状态加载设置为 true,因此您可以显示加载器直到获取结果。
但是,您可以在useEffect的清理功能中删除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
只是更新。
如果您的主要关注点是确保用户没有看到不正确的结果,您可以让传递的函数返回一个数组及其原始参数和答案,然后检查以确保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; }
不理想,但在某些用例中有效。