본문 바로가기

카테고리 없음

[Epic React][Build an Epic React App][Compound Context]

반응형

 

bookshelf/INSTRUCTIONS.md at exercises/08-compound-components · kentcdodds/bookshelf (github.com)

 

GitHub - kentcdodds/bookshelf: Build a ReactJS App workshop

Build a ReactJS App workshop. Contribute to kentcdodds/bookshelf development by creating an account on GitHub.

github.com

Background


재사용하려고 만든 컴포넌트들이 십중팔구 props가 너무 많아서 재사용하기 어렵게 된다.

컴포넌트를 Tab, Card, Modal과 같은 단위로 만들고 (Organisms?)

해당 컴포넌트 내부의 상태를 암시적으로 공유하게 한다.

그리고 명시적인 내부 컴포넌트 API를 제공한다.

 

실습 : 모달 만들기


/** @jsx jsx */
import {jsx} from '@emotion/core'

import * as React from 'react'
import VisuallyHidden from '@reach/visually-hidden'
import {Dialog, CircleButton} from './lib'

// 사용자가 onClick을 주입한 경우 호출을 위해 사용한다.
const callAll = (...fns) => (...args) => fns.forEach(fn => fn && fn(...args))

const ModalContext = React.createContext()

function Modal(props) {
  const [isOpen, setIsOpen] = React.useState(false)

  return <ModalContext.Provider value={[isOpen, setIsOpen]} {...props} />
}
function ModalDismissButton({children: child}) {
  const [, setIsOpen] = React.useContext(ModalContext)
  return React.cloneElement(child, {
    onClick: callAll(() => setIsOpen(false), child.props.onClick),
  })
}

function ModalOpenButton({children: child}) {
  const [, setIsOpen] = React.useContext(ModalContext)
  return React.cloneElement(child, {
    onClick: callAll(() => setIsOpen(true), child.props.onClick),
  })
}

function ModalContentsBase(props) {
  const [isOpen, setIsOpen] = React.useContext(ModalContext)
  return (
    <Dialog isOpen={isOpen} onDismiss={() => setIsOpen(false)} {...props} />
  )
}

function ModalContents({title, children, ...props}) {
  return (
    <ModalContentsBase {...props}>
      <div css={{display: 'flex', justifyContent: 'flex-end'}}>
        <ModalDismissButton>
          <CircleButton>
            <VisuallyHidden>Close</VisuallyHidden>
            <span aria-hidden>×</span>
          </CircleButton>
        </ModalDismissButton>
      </div>
      <h3 css={{textAlign: 'center', fontSize: '2em'}}>{title}</h3>
      {children}
    </ModalContentsBase>
  )
}

export {Modal, ModalDismissButton, ModalOpenButton, ModalContents, ModalContentsBase}

ModalContents는 디폴트 컨텐츠를 제공해줌.

ModalContentsBase는 버튼 없는 모달 상태부터 만들 시에 사용

ModalContext는 이런식으로 나옴. 모달 윗부분을 공통으로 적용함

ModalContentsBase(여기에서는 ModalContents) 사용 케이스

bookshelf/unauthenticated-app.final.js at exercises/08-compound-components · kentcdodds/bookshelf (github.com)

 

GitHub - kentcdodds/bookshelf: Build a ReactJS App workshop

Build a ReactJS App workshop. Contribute to kentcdodds/bookshelf development by creating an account on GitHub.

github.com

 

 

ModalContents 사용 케이스

bookshelf/unauthenticated-app.extra-2.js at exercises/08-compound-components · kentcdodds/bookshelf (github.com)

 

GitHub - kentcdodds/bookshelf: Build a ReactJS App workshop

Build a ReactJS App workshop. Contribute to kentcdodds/bookshelf development by creating an account on GitHub.

github.com

 

모달의 경우, 전역상태로 open을 공유한다.

탭의 경우, 열린 탭 같은 포커싱 상태를 공유할 것이다.

Compound Component 패턴을 이용하면, 사용자는 해당 상태에 대해 전혀 모르고 컴포넌트를 사용할 수 있다.

 

참고 : 

Simply React

React Hooks: Compound Components (kentcdodds.com)

 

React Hooks: Compound Components

How do compound components change with React hooks?

kentcdodds.com

 

반응형