発生現象
Material-UIのDialogの中身を自作のコンポーネントを表示させようとしたところ、警告がでた。
警告内容
ソースコード
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>
);
}