以前、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>
);
}