LoginSignup
1
2

More than 3 years have passed since last update.

React Material の初期開発で<Dialog>いっぱい書きたくない話

Posted at
概要

dialogやsnackbarをmaterialで記載する時にcontextを利用すると再利用生があがるので、初期開発の速度が上がる可能性がある。

経緯

個人的にも昔のProjectで、[ダイアログ空いてる?、] = useState()てきなことを大量にかいていた戒め。

TL;DR

ReactのClasss Componentを利用してContext Providerを作成。
component側からは、DialogのReactElementを渡す。
ClassComponentを使わない場合については、hooksが利用できないので(hooks in hooksでエラー)死んだ話。

流れ

  1. Reactのプロジェクトを作成
  2. Materialを追加
  3. Class Componentを利用してContextを作成
  4. Dialogの中身を作成(適当に)
  5. Functional Componen側からDialogContextを呼ぶ

1. Reactのプロジェクトを作成

Nxまたはcreate-react-appを利用してアプリケーションを作成

create-react-appを利用する場合

$ create-react-app my-app

nxを利用する場合
terminal
$ npx create-nx-workspace@latest --preset=react

プロジェクトが作成されたら、Materialの追加

2. Materialを追加

$ yarn add @material-ui/core

ここで下準備終了。

3. Class Componentを利用してContextを作成

stateにDialogに必要なopenのflag, elementを利用、
contextValueのcreate, closeのfuncitonを持たせる。
dialogにoptionを追加した場合は、default のoptionを内部で持ちcreateの時にoverrideするといろいろ楽に対応できます。

contexts/dialog-context.tsx
import { Dialog } from '@material-ui/core';
import React, { createContext } from 'react';

// ?はdefaultValueの時に渡せないため、?を利用
export interface DialogContextValue {
  createDialog?: (elm: JSX.Element) => void,
  closeDialog?: () => void,
  open: boolean
}

/* eslint-disable-next-line */
export interface DialogContexttProps {}

interface DialogContextState {
  open: boolean,
  elm: JSX.Element
}

export const DialogContext = createContext<DialogContextValue>({
  open: false
})

export class DialogcontextProvider extends React.Component<DialogContexttProps, DialogContextState> {
  constructor(props) {
    super(props)
    this.state = {
      open: false,
      elm: null
    }
  }

  createDialog = (elm: JSX.Element) => {
    this.setState(state => ({
      ...state,
      open: true,
      elm
    }))
  }

  closeDialog = () => {
    this.setState(state => ({
      ...state,
      open: false,
      elm: null
    }))
  }

  render() {
    return (
      <DialogContext.Provider value={{
        open: this.state.open,
        createDialog: this.createDialog,
        closeDialog: this.closeDialog
      }}>
        { this.props.children }
        <Dialog open={this.state.open}>
          { this.state.elm }
        </Dialog>
      </DialogContext.Provider>
    )
  }
}

export default DialogcontextProvider;

次にcontextを利用するため、Appに追記します。
適宜利用してください。

app.tsx
import React from 'react';

import DialogcontextProvider from './contexts/dialog-contextt';

const App = () => (
  <div>
    <DialogcontextProvider>
      <div>sample</div>
    </DialogcontextProvider>
  </div>
)

export default App

4. Dialogの中身を作成(適当に)

これは、適当でいいですがhooksを内部で利用するとclass Componentじゃないといけないことがわかると思います。
useForm, useDispatch, useSelectorなど結構メジャーどころを使ってcontextProvider ClassComponentじゃない時に、hook in hookのエラーが表示されます。

components/dialog-content.tsx
import React from 'react';

/* eslint-disable-next-line */
export interface DialogContentProps {}

export function DialogContent(props: DialogContentProps) {
  return (
    <div>
      <h1>Welcome to dialog-content!</h1>
    </div>
  );
}

export default DialogContent;

5. Functional Componen側からDialogContextを呼ぶ

最後に呼び出し元のcomponentを作成して、dialog呼べるか確認していきます。

components/button.tsx
import { DialogContent } from './dialog-content';
import React, { useContext } from 'react';
import { DialogContext } from '../contexts/dialog-contextt';

/* eslint-disable-next-line */
export interface ButtonProps {}

export function Button(props: ButtonProps) {
  const { createDialog } = useContext(DialogContext)
  return (
    <div>
      <button onClick={() => createDialog(<DialogContent />)}>click to open</button>
    </div>
  );
}

export default Button;

app.tsxに呼び出し元を追加。

app.tsx
import React from 'react';
import Button from './components/button';

import DialogcontextProvider from './contexts/dialog-contextt';

const App = () => (
  <div>
    <DialogcontextProvider>
      <div>sample</div>
      <Button />
    </DialogcontextProvider>
  </div>
)

export default App

表示されたbuttonをクリックした時にelementが表示されます。

dialog-none.png

opened-dialog.png

地味に利用手段が増えるので、snackbarとかも、入れておくと、

Sample.tsx
function Sample () {
  useEffect(() => {
      createSnackBar({ type: 'error', message: "Api Failっぽいよ" })
  }, [errors])
  return (
  )
}

の様にcomponent側から呼び出すことが可能になり、Componentは一つのため、開発速度は上がります。

Repo

1
2
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
2