【问题标题】:How to create a generic "consecutive snackbars" component with React and Material UI?如何使用 React 和 Material UI 创建通用的“连续小吃店”组件?
【发布时间】:2023-02-17 20:44:48
【问题描述】:

Material UI 文档有 a section about "Consecutive Snackbars",但没有说明我们如何将逻辑提取到通用组件,以便我们可以在应用程序的多个地方使用它。

我正在使用 React v18 和 Material UI v5。

【问题讨论】:

    标签: reactjs material-ui snackbar


    【解决方案1】:

    一个有效的例子是根据传递的道具(即content,重命名为value)替换MUI documentation(即handleClick)的“点击”事件处理程序:

    // src/Snack.tsx
    import { Snackbar, SnackbarProps } from "@mui/material";
    import React from "react";
    
    type SnackContent = {
      key: number;
      value: React.ReactNode;
    };
    
    // Omit all props necessary for the consecutive snackbars logic
    type SnackProps = Omit<
      SnackbarProps,
      "children" | "key" | "message" | "onClose" | "open" | "TransitionProps"
    > & {
      content: SnackContent["value"];
    };
    
    export const Snack: React.FC<SnackProps> = ({
      // Passed `content` is actually the `SnackContent.value`
      content: value,
      ...otherProps
    }) => {
      const [content, setContent] = React.useState<SnackContent>();
      const [pack, setPack] = React.useState<readonly SnackContent[]>([]);
      const [isOpen, setIsOpen] = React.useState<boolean>(false);
    
      const handleSnackClose = (
        event: React.SyntheticEvent | Event,
        reason?: string
      ) => reason !== "clickaway" && setIsOpen(false);
    
      const handleSnackExited = () => setContent(undefined);
    
      // Update content pack
      React.useEffect(() => {
        value && setPack((prev) => [...prev, { key: new Date().getTime(), value }]);
      }, [value]);
    
      // Handle consecutive snackbars https://mui.com/material-ui/react-snackbar/#consecutive-snackbars
      React.useEffect(() => {
        if (pack.length && !content) {
          // Set a new snack when we don't have an active one
          setContent({ ...pack[0] });
          setPack((prev) => prev.slice(1));
          setIsOpen(true);
        } else if (pack.length && content && isOpen) {
          // Close an active snack when a new one is added
          setIsOpen(false);
        }
      }, [pack, content, isOpen]);
    
      return (
        <Snackbar
          key={content?.key}
          open={isOpen}
          autoHideDuration={6000}
          onClose={handleSnackClose}
          TransitionProps={{ onExited: handleSnackExited }}
          {...otherProps}
        >
          {/* A "div" wrapper is required so `content.value` can be `null` */}
          <div>{content?.value}</div>
        </Snackbar>
      );
    };
    

    用法:

    // src/SomeComponent.tsx
    import React from "react";
    import { Snack } from "./Snack";
    
    export const SomeComponent: React.FC = () => {
      const [snackContent, setSnackContent] = React.useState<React.ReactNode>();
    
      // The "hello world" text is wrapped with React.Fragment so the `Snack` component rerenders when its `content` prop value changes
      const handleTestClick = () => setSnackContent(<>"Hello, world!"</>);
    
      return (
        <>
          <button onClick={handleTestClick}>Test</button>
          <Snack
            content={snackContent}
            anchorOrigin={{ horizontal: "center", vertical: "bottom" }}
          />
        </>
      );
    };
    

    这适用于经典和触摸桌面。

    这是a code sandbox

    【讨论】:

      猜你喜欢
      • 2021-02-24
      • 2020-12-15
      • 1970-01-01
      • 1970-01-01
      • 2017-10-04
      • 2016-04-02
      • 2019-01-29
      • 2020-11-18
      • 2019-02-28
      相关资源
      最近更新 更多