以前、AppBar と Drawer を組み合わせた開閉メニューを含む画面レイアウトを紹介しておりました。
React: MUI AppBar と Drawer を組み合わせた開閉メニューの作成 (サンプルあり) | Water Margin
動作イメージは次の通りです。
上記の記事では useContext は利用しておらず、App.tsx に AppBar と Drawer が全て記述されておりました。よって、複雑なステート管理もなく、開閉ステートは AppBar 上の変数(useState フック)で済ませておりました。
AppBar と Drawer の連携方法を理解する上では、App.tsx にまとめて記述されているとコードを理解しやすいのですが、実際にアプリケーションを作る際には、AppBar、Drawer は別々のコンポーネントとして独立していると思われます。本稿では、AppBar、Drawer を独立したコンポーネントとして用意し、これらコンポーネント間のステート管理に useContext を利用します。
AppState.ts の実装(アプリケーション全体で共有されるステート)
AppState.ts の中で createContext メソッドを利用し、メニュー開閉のステートを管理する menuContext を用意します。
import { createContext } from "react"; export const menuContext = createContext({ isOpened: false, setOpened: (isOpen: boolean) => {} });
MyAppBar.tsx の実装(MUI AppBar を含むコンポーネント)
AppState.ts の中で用意した menuContext を、MyAppBar.tsx の中で参照します。AppBar 上のハンバーガーメニューアイコンをクリックした際に、toggleOpen メソッドが呼び出され、menuContext の値が切り替わります。
... export const MyAppBar = () => { // AppState.ts の menuContext を引数に与える const { isOpened, setOpened } = useContext(menuContext); // menuContext の isOpened プロパティを切り替える const toggleOpen = () => setOpened(!isOpened); return ( <> <Box sx={{ display: "flex" }}> <AppBar position="fixed" sx={{ zIndex: (theme) => theme.zIndex.drawer + 1 }} > <Toolbar> <IconButton ... onClick={toggleOpen} sx={{ mr: 2 }} > <MenuIcon /> </IconButton> <Typography ... > タイトル </Typography> </Toolbar> </AppBar> </Box> </> ); };
MyDrawer.tsx の実装(MUI Drawer を含むコンポーネント)
MyDrawer.tsx 側では、MUI Drawer の開閉状態は Props でシンプルに受け取るようにしておきます。受け取る値(open プロパティ)は、MUI Drawer の open プロパティにバインドしておきます。
type Props = { open: boolean; }; export const MyDrawer = (props: Props) => { return ( <> <Drawer ... open={props.open} > <Toolbar /> <Box sx={{ overflow: "auto" }}> <List> <ListItem button> <ListItemText primary="メニュー1" /> </ListItem> <ListItem button> <ListItemText primary="メニュー2" /> </ListItem> </List> </Box> </Drawer> </> ); };
App.tsx の実装(MyAppBar、MyDrawer を含むコンポーネント)
App.tsx の App() では、menuContext のプロバイダを通じて React コンテキストを利用できるようにしておきます。その上で、ステートを読み取るコンポーネントである MyDrawer と Main には、isOpened プロパティを渡します。
export default function App() { const [isOpened, setOpened] = React.useState(true); return ( <menuContext.Provider value={{ isOpened, setOpened }}> <Box sx={{ display: "flex" }}> <CssBaseline /> {/* AppBar */} <MyAppBar /> {/* Drawer */} <MyDrawer open={isOpened} /> {/* Main */} <Main open={isOpened}> <Toolbar /> <div>メイン</div> </Main> </Box> </menuContext.Provider> ); }