1
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 1 year has passed since last update.

ReactでMaterial-UI Dialogを使ったときのワーニング解消

Last updated at Posted at 2022-03-06

発生現象

Material-UIのDialogの中身を自作のコンポーネントを表示させようとしたところ、警告がでた。

警告内容

スクリーンショット 2022-03-05 20.36.50.png

ソースコード

App.jsx

import React, { useEffect, useState } from "react";
import Dialog from "@mui/material/Dialog";
import DialogActions from "@mui/material/DialogActions";
import DialogContent from "@mui/material/DialogContent";
import DialogTitle from "@mui/material/DialogTitle";
import Button from "@mui/material/Button";
import ShowAlert from "./ShowAlert";

export default function App() {
  const alertRef = React.createRef();
  const [show, setShow] = useState(true);
  const [errored, setErrored] = useState(false);
  const [showId, setShowId] = useState("");

  const closeDialog = () => {
    setShow(false);
  };
  const switchAlert = () => {
    setErrored(!errored);
  };
  const choiceHello = () => {
    setShowId("hello");
  };
  const choiceBanana = () => {
    setShowId("banana");
  };

  useEffect(() => {
    if (errored) {
      alertRef.current.error("エラーだよ!");
    }
  }, [errored]);

  return (
    <div>
      <h1>Hello CodeSandbox</h1>
      <Dialog open={show} onClose={closeDialog}>
        <DialogTitle>Test</DialogTitle>
        <DialogContent>
          {errored && <ShowAlert ref={alertRef} />}
          {errored ||
            (showId === "hello" && <h1>Hello</h1>) ||
            (showId === "banana" && <h2>Banana</h2>)}
        </DialogContent>
        <DialogActions>
          <Button onClick={choiceHello}>Hello</Button>
          <Button onClick={choiceBanana}>Banana</Button>
          <Button onClick={switchAlert}>エラー表示</Button>
          <Button onClick={closeDialog}>Close</Button>
        </DialogActions>
      </Dialog>
    </div>
  );
}

ShowAlert.jsx

import React from "react";
import Alert from "@mui/material/Alert";
import Stack from "@mui/material/Stack";

const ShowAlert = (props, ref) => {
  const [message, setMessage] = React.useState(null);
  const [severity, setSeverity] = React.useState("error");

  const close = () => {
    setMessage(null);
    setSeverity("error");
  };

  React.useImperativeHandle(ref, () => ({
    error(message) {
      setMessage(message);
      setSeverity("error");
    }
  }));

  return (
    <div>
      <Stack
        sx={{
          width: "100%",
          display: message ? "initial" : "none"
        }}
        spacing={2}
      >
        <Alert
          severity={severity}
          onClose={() => {
            close();
          }}
        >
          {message}
        </Alert>
      </Stack>
    </div>
  );
};

export default React.forwardRef(ShowAlert);

package.json

{
  "name": "react",
  "version": "1.0.0",
  "description": "React example starter project",
  "keywords": [
    "react",
    "starter"
  ],
  "main": "src/index.js",
  "dependencies": {
    "@emotion/react": "11.8.1",
    "@emotion/styled": "11.8.1",
    "@mui/icons-material": "5.4.4",
    "@mui/material": "5.4.4",
    "react": "17.0.2",
    "react-dom": "17.0.2",
    "react-scripts": "4.0.0"
  },
  "devDependencies": {
    "@babel/runtime": "7.13.8",
    "typescript": "4.1.3"
  },
  "scripts": {
    "start": "react-scripts start",
    "build": "react-scripts build",
    "test": "react-scripts test --env=jsdom",
    "eject": "react-scripts eject"
  },
  "browserslist": [
    ">0.2%",
    "not dead",
    "not ie <= 11",
    "not op_mini all"
  ]
}

解決方法

参考にした記事

https://blog.gaji.jp/2021/01/08/6247/
公式: https://mui.com/guides/composition/#caveat-with-refs
記事によると、Dialogはツリー構造の外側にノードを移動してダイアログを表示するため、各種の参照が途切れてしまうのが原因。ダイアログを開いた時に提案されるforwardRefを使って、参照を自作コンポーネントのrootのHTML要素に渡すことで解決できるそう。
何を言っているのか理解できてないが下記のようにコードを修正したら警告がきえた。

おためし

DialogContentの中を全部React.fowardRefでかこんでみたら、警告が表示されなくなった。

App.jsx

import React, { useEffect, useState } from "react";
import Dialog from "@mui/material/Dialog";
import DialogActions from "@mui/material/DialogActions";
import DialogContent from "@mui/material/DialogContent";
import DialogTitle from "@mui/material/DialogTitle";
import Button from "@mui/material/Button";
import ShowAlert from "./ShowAlert";

export default function App() {
  const alertRef = React.createRef();
  const [show, setShow] = useState(true);
  const [errored, setErrored] = useState(false);
  const [showId, setShowId] = useState("");

  const closeDialog = () => {
    setShow(false);
  };

  const switchAlert = () => {
    setErrored(!errored);
  };
  const choiceHello = () => {
    setShowId("hello");
  };
  const choiceBanana = () => {
    setShowId("banana");
  };

  const WrapperContainer = React.forwardRef(({ errored }, ref) => {
    if (errored) {
      console.log(errored);
      return <ShowAlert ref={ref} />;
    } else {
      if (showId === "hello") {
        console.log("hello");
        return <h1>Hello</h1>;
      }
      if (showId === "banana") {
        console.log("banana");
        return <h2>Banana</h2>;
      } else {
        return null;
      }
    }
  });

  useEffect(() => {
    if (errored) {
      alertRef.current.error("errorrrrrrrrrr");
    }
  }, [errored]);

  return (
    <div>
      <h1>Hello CodeSandbox</h1>
      <Dialog open={show} onClose={closeDialog}>
        <DialogTitle>Test</DialogTitle>
        <DialogContent>
          <WrapperContainer errored={errored} ref={alertRef} />
        </DialogContent>
        <DialogActions>
          <Button onClick={choiceHello}>Hello</Button>
          <Button onClick={choiceBanana}>Banana</Button>
          <Button onClick={switchAlert}>エラー表示</Button>
          <Button onClick={closeDialog}>Close</Button>
        </DialogActions>
      </Dialog>
    </div>
  );
}
1
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
1
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?