我使用 Material-ui(和 Typescript)在 React 中制作了一个自定义 Snackbar 组件。我无法理解扩展运算符和等待如何工作。 (此处示例:https://codesandbox.io/s/gifted-hopper-tkkpi?file=/src/App.tsx:1311-1380)
完整代码:
import React, { FunctionComponent, useEffect } from "react";
import Button from "@material-ui/core/Button";
import Snackbar from "@material-ui/core/Snackbar";
import { Color } from "@material-ui/lab";
import { Alert } from "@material-ui/lab";
interface SnackbarModel {
open: boolean;
severity?: Color;
message: string;
}
const CustomSnackbar: FunctionComponent<SnackbarModel> = (props) => {
const [opened, setOpened] = React.useState(false);
useEffect(() => {
if (props.open) {
setOpened(true);
}
}, [props.open]);
const handleClose = (event: React.ChangeEvent<object>, reason: string) => {
if (reason === "clickaway") {
return;
}
setOpened(false);
};
return (
<div>
<Snackbar
open={opened}
autoHideDuration={2000}
onClose={handleClose}
anchorOrigin={{ vertical: "top", horizontal: "center" }}
>
<Alert severity={props.severity}>{props.message}</Alert>
</Snackbar>
</div>
);
};
export default function App() {
const [snackbar, setSnackbar] = React.useState<SnackbarModel>({
open: false,
message: ""
});
async function handleClick() {
await fetch(Stuff...)
// Working as desired
//setSnackbar({open: true, severity: "success", message: "Snackbar"});
//setSnackbar({open: false, severity: "success", message: "Snackbar"});
// Somehow not working
setSnackbar({ open: true, severity: "success", message: "Snackbar" });
setSnackbar({ ...snackbar, open: false });
}
return (
<div>
<CustomSnackbar
open={snackbar.open}
message={snackbar.message}
severity={snackbar.severity}
/>
<Button variant="contained" onClick={handleClick}>
Snackbar
</Button>
</div>
);
}
我触发 Snackbar 打开
const [snackbar, setSnackbar] = React.useState<SnackbarModel>({
open: false,
message: ""
});
async function handleClick() {
await fetch(Stuff)
setSnackbar({open: true, severity: "success", message: "Snackbar"})
setSnackbar({open: false, severity: "success", message: "Snackbar"});
}
Snackbar 在设定的时间后自动关闭。 “open”仅触发 Snackbar,不控制 Snackbar 的 setOpen-state。
现在我尝试了:
setSnackbar({ open: true, severity: "success", message: "Snackbar" });
console.log(snackbar); //gives you: Object {open: false, message: ""}
setSnackbar({ ...snackbar, open: false });
这成功触发了 Snackbar,但消息/文本丢失了。
问题 1:“...snackbar”不应该仍然保存“message: 'Snackbar'”吗?
出于某种原因,如果我不使用“await fetch(Stuff...):”,我还必须使函数异步并等待第一个“setSnackbar”:
async function handleClick() {
//working as desired
await setSnackbar({open: true, severity: "success", message: "Snackbar"});
setSnackbar({open: false, severity: "success", message: "Snackbar"});
// OR
//not working as desired
await setSnackbar({ open: true, severity: "success", message: "Snackbar" });
setSnackbar({ ...snackbar, open: false });
}
尽管如果您首先等待另一行代码,则显然不需要“setSnackbar”上的等待。在我的用例中,这是有效的:
async function handleClick() {
await fetch(Stuff...)
setSnackbar({open: true, severity: "success", message: "Snackbar"});
setSnackbar({open: false, severity: "success", message: "Snackbar"});
}
问题 2:有人可以解释为什么需要/不需要等待吗?
最佳答案
实现中的主要问题是 setSnackbar
的处理方式就好像它是同步的,但它“有点”不是。这里详细描述了原因:useState set method not reflecting change immediately (尤其是第二个答案)
答案 1:
展开运算符不起作用,因为大多数时候,第一个 setSnackbar 没有完成。
为了解决此问题,您应该使用 useEffect
方法将 Snackback 对象的 open
属性作为数组中的依赖项传递。
您应该执行与 CustomSnackbar 中相同的操作:
useEffect(() => {
if (snackbar.open) {
setSnackbar((_snackbar) => ({ ..._snackbar, open: false }));
}
}, [snackbar.open]);
注意_snackbar
,它在setSnackbar的回调中使用来获取当前的open属性值。
答案2:关于await。
API 调用需要 await
,因为 api 调用是异步/Promises,这意味着程序将在完成之前执行下一个命令,因此您需要“等待”它们,或者完成后使用 .then() 做一些事情。
setSnackbar
不是一个 promise ,但它不会立即生效,这就是为什么 React 有 setState 和 useEffect
的回调。来处理此类案件。因此,在 setSnackbar
之前使用 await
没有什么区别
关于javascript - Spread 运算符无法与 setState 一起使用(React、Typescript 和 Material-ui),我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/64138873/