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">
我正在使用一个自定义的react钩子来调用API并在我的react组件中设置状态。钩子接受一个函数,该函数返回promise和依赖数组。挂钩返回[loading, result, error, reload, setState]
。我希望该结果将在钩子更改的依赖关系发生后立即重置,但是一个渲染的状态仍然陈旧,这破坏了组件中的逻辑。
重现步骤。
I.假设数字为2,则其平方为4。
二。打开控制台。
三,增量2,数字将变为3,在一秒后变为9。
IV。在控制台中,您会看到类似 2 4 , 3 4 , 3空, 3 9
V. 3 4 是错误的日志。
答案 0 :(得分:1)
您会在控制台中看到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);
答案 1 :(得分:0)
当您多次单击递增或递减按钮时,您将有多个未完成的承诺。然后,result
会在解决每个承诺时简单地进行更新。
如果您最关心的是确保用户看不到错误的结果,则可以让传递的函数返回一个带有其原始参数以及答案的数组,然后检查以确保{{ 1}}与之匹配:
number
不太理想,但可以在某些用例中使用。