javascript - Spread 运算符无法与 setState 一起使用(React、Typescript 和 Material-ui)

标签 javascript html reactjs typescript material-ui

我使用 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/

相关文章:

javascript - 不同尺寸的迷你图?

javascript - 使用 JavaScript 在网站上创建推送通知

javascript - 如何在页面加载时自动调用按钮提交

javascript - 使用 CSS 或 JavaScript 缩放背景图像

javascript - iPhone 不支持内联视频 - 是否可以使用 JS 从上传的视频文件中去除帧和音频?

reactjs - 在 React/Bootstrap 4 中,禁用按钮以防止重复表单提交的正确方法是什么?

javascript - 无法弄清楚如何使用 {this.props.etc} 尤其是 React.js 中的 map

javascript - jQuery 检查 CSS 宽度是否大于 0 然后添加 CSS 属性

javascript - MEAN Stack,为什么express在url中保留带有#的路由

html - IE8 在鼠标悬停时移动内容