当按钮类型为 "submit" 时,useState 挂钩不会触发
When button type is "submit", useState hook doesn't fire
如标题所示,我有一个 form
组件,它有一个 onSubmit 属性集来处理表单提交。通常在我的应用程序中,当用户提交表单时,会启动一个很长的 运行 过程。在那段时间里,我想显示一个 spinner/disable 按钮来阻止用户在查询加载时提交表单。
但是,当提交 button
类型为 submit
时,来自 useState
挂钩(甚至是 Redux 调度程序)的任何状态设置器似乎都不会更改状态。
我看到调用提交处理程序时组件为 re-rendered,即使我使用了 event.preventDefault()
和 event.stopPropagation()
。我怎样才能防止这种 re-render 发生并扰乱状态?
我的代码如下,I have create a minimum reproduction in a Code Sandbox here. 我使用了一个 setTimeout
函数来模拟实际发生的 API 调用。
import React, {
ChangeEvent,
FormEvent,
FunctionComponent,
useRef,
useState
} from "react";
import { Button, Form, Modal, Spinner } from "react-bootstrap";
const SimpleForm: FunctionComponent = () => {
// form state
const [name, setName] = useState("TestName");
const [age, setAge] = useState("10");
// form state helpers
const [submitLoading, setSubmitLoading] = useState(false);
const formRef = useRef<HTMLFormElement | null>(null);
const handleSubmit = (event: FormEvent<HTMLFormElement>) => {
event.preventDefault();
event.stopPropagation();
const form = event.currentTarget;
if (form.checkValidity()) {
setSubmitLoading(true);
console.log("valid form");
setTimeout(() => console.log("blah"), 5000);
setSubmitLoading(false);
}
};
return (
<Modal centered size="lg" show={true}>
<Form ref={formRef} onSubmit={handleSubmit}>
<Modal.Header closeButton>
<Modal.Title>Simple Form</Modal.Title>
</Modal.Header>
<Modal.Body>
<Form.Group className="mb-3" controlId="instanceName">
<Form.Label as="h6">Name</Form.Label>
<Form.Control
required
type="text"
placeholder="Instance name here"
value={name}
spellCheck="false"
autoComplete="off"
onChange={(e) => {
setName(e.target.value);
}}
/>
</Form.Group>
<Form.Group className="mb-3" controlId="diskSize">
<Form.Label as="h6">Age</Form.Label>
<Form.Control
type="number"
min="10"
max="1000"
value={age}
onChange={(e: ChangeEvent<HTMLInputElement>) =>
setAge(e.target.value)
}
/>
</Form.Group>
</Modal.Body>
<Modal.Footer>
<Button variant="secondary">Close</Button>
<Button
type="submit"
variant="success"
style={{
padding: submitLoading ? "0.375rem 3.875rem" : "0.375rem 0.8rem"
}}
>
{submitLoading ? (
<Spinner size="sm" animation="border" />
) : (
"Submit Form"
)}
</Button>
</Modal.Footer>
</Form>
</Modal>
);
};
我试过用一个闭包替换函数的内部,有和没有以前的状态 (() => true
/ (prevState) => !prevState
)。我尝试用 useRef
挂钩替换 useState
挂钩,并在事件处理函数中手动设置 ref。我也将状态转移到 Redux 中,但这似乎也没有做任何事情。我还尝试向 button
添加一个 onClick
道具,里面有一个闭包,但似乎也没有任何作用。
如有任何帮助,我们将不胜感激!
一个问题是setTimeout在这里什么都不做,因为它作为setSubmitLoading函数在5000ms之后运行,js基本上“忽略”这里的setTimeout,除非你使用await,这使得它等待函数完成(使用等待实际的 api 请求,它不适用于 setTimeout)
这意味着 React 立即更新状态并没有真正改变任何东西
这表明您的 onSubmit 函数可能没有正确使用 await,或者 .then Promise 回调
asyncFunction().then(() => setState(false))
您在将加载状态设置为 true 后立即将其设置为 false,因此您看不到任何变化。如果您将 setSubmitLoading(false)
移到 setTimeOut
中,更改将反映给您,就像我在沙盒中所做的那样。
const handleSubmit = () => {
console.log(`submitLoading in handler: ${submitLoading}`);
setSubmitLoading(true);
console.log("valid form");
setTimeout(() => setSubmitLoading(false), 5000);
};
如标题所示,我有一个 form
组件,它有一个 onSubmit 属性集来处理表单提交。通常在我的应用程序中,当用户提交表单时,会启动一个很长的 运行 过程。在那段时间里,我想显示一个 spinner/disable 按钮来阻止用户在查询加载时提交表单。
但是,当提交 button
类型为 submit
时,来自 useState
挂钩(甚至是 Redux 调度程序)的任何状态设置器似乎都不会更改状态。
我看到调用提交处理程序时组件为 re-rendered,即使我使用了 event.preventDefault()
和 event.stopPropagation()
。我怎样才能防止这种 re-render 发生并扰乱状态?
我的代码如下,I have create a minimum reproduction in a Code Sandbox here. 我使用了一个 setTimeout
函数来模拟实际发生的 API 调用。
import React, {
ChangeEvent,
FormEvent,
FunctionComponent,
useRef,
useState
} from "react";
import { Button, Form, Modal, Spinner } from "react-bootstrap";
const SimpleForm: FunctionComponent = () => {
// form state
const [name, setName] = useState("TestName");
const [age, setAge] = useState("10");
// form state helpers
const [submitLoading, setSubmitLoading] = useState(false);
const formRef = useRef<HTMLFormElement | null>(null);
const handleSubmit = (event: FormEvent<HTMLFormElement>) => {
event.preventDefault();
event.stopPropagation();
const form = event.currentTarget;
if (form.checkValidity()) {
setSubmitLoading(true);
console.log("valid form");
setTimeout(() => console.log("blah"), 5000);
setSubmitLoading(false);
}
};
return (
<Modal centered size="lg" show={true}>
<Form ref={formRef} onSubmit={handleSubmit}>
<Modal.Header closeButton>
<Modal.Title>Simple Form</Modal.Title>
</Modal.Header>
<Modal.Body>
<Form.Group className="mb-3" controlId="instanceName">
<Form.Label as="h6">Name</Form.Label>
<Form.Control
required
type="text"
placeholder="Instance name here"
value={name}
spellCheck="false"
autoComplete="off"
onChange={(e) => {
setName(e.target.value);
}}
/>
</Form.Group>
<Form.Group className="mb-3" controlId="diskSize">
<Form.Label as="h6">Age</Form.Label>
<Form.Control
type="number"
min="10"
max="1000"
value={age}
onChange={(e: ChangeEvent<HTMLInputElement>) =>
setAge(e.target.value)
}
/>
</Form.Group>
</Modal.Body>
<Modal.Footer>
<Button variant="secondary">Close</Button>
<Button
type="submit"
variant="success"
style={{
padding: submitLoading ? "0.375rem 3.875rem" : "0.375rem 0.8rem"
}}
>
{submitLoading ? (
<Spinner size="sm" animation="border" />
) : (
"Submit Form"
)}
</Button>
</Modal.Footer>
</Form>
</Modal>
);
};
我试过用一个闭包替换函数的内部,有和没有以前的状态 (() => true
/ (prevState) => !prevState
)。我尝试用 useRef
挂钩替换 useState
挂钩,并在事件处理函数中手动设置 ref。我也将状态转移到 Redux 中,但这似乎也没有做任何事情。我还尝试向 button
添加一个 onClick
道具,里面有一个闭包,但似乎也没有任何作用。
如有任何帮助,我们将不胜感激!
一个问题是setTimeout在这里什么都不做,因为它作为setSubmitLoading函数在5000ms之后运行,js基本上“忽略”这里的setTimeout,除非你使用await,这使得它等待函数完成(使用等待实际的 api 请求,它不适用于 setTimeout)
这意味着 React 立即更新状态并没有真正改变任何东西
这表明您的 onSubmit 函数可能没有正确使用 await,或者 .then Promise 回调
asyncFunction().then(() => setState(false))
您在将加载状态设置为 true 后立即将其设置为 false,因此您看不到任何变化。如果您将 setSubmitLoading(false)
移到 setTimeOut
中,更改将反映给您,就像我在沙盒中所做的那样。
const handleSubmit = () => {
console.log(`submitLoading in handler: ${submitLoading}`);
setSubmitLoading(true);
console.log("valid form");
setTimeout(() => setSubmitLoading(false), 5000);
};