MUI React プログラミング

React MUI: Drawer の開閉を useContext でステート管理する(サンプルあり)

2022年5月7日

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

サンプルアプリケーション

-MUI, React, プログラミング