Context
https://ko.reactjs.org/docs/context.html#when-to-use-context
- context를 이용하면 단계마다 일일이 props를 넘겨주지 않고도 컴포넌트 트리 전체에 데이터를 제공할 수 있다
언제?
- React 컴포넌트 트리 안에서 전역적(global)이라고 볼 수 있는 데이터를 공유할 수 있도록 고안된 방법
- 그러한 데이터로는 현재 로그인한 유저, 테마, 선호하는 언어 등이 있다
글로벌로 사용할 Theme
1 2 3 4 5 6 7 8 9 10 11 12 13
| export const themes = { light: { foreground: '#000000', background: '#eeeeee', }, dark: { foreground: '#ffffff', background: '#222222', }, };
export const ThemeContext = React.createContext(themes.dark);
|
ThemeChange Btn
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16
| import React, { useContext } from 'react'; import { ThemeContext } from './ThemeContext';
export default function ThemedButton(props) { const theme = useContext(ThemeContext); return ( <button {...props} style={{ backgroundColor: theme.background, color: theme.foreground }} onClick={props.onClick} > ThemedButton </button> ); }
|
Theme Component
- Themecontext.Provider 안쪽의 버튼은 state값이 themes.light 스타일을 갖는다
- Themecontext.Provider 바깥쪽 버튼은 ThemeContext의 디폴트 값이 dark를 스타일로 갖는다
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30
| import { ThemeContext, themes } from './ThemeContext'; import ThemedButton from './ThemedButton'; import React, { useState } from 'react';
export default function Theme() { const [theme, setTheme] = useState(themes.light); const toggleTheme = () => { setTheme((prev) => (prev === themes.dark ? themes.light : themes.dark)); }; return ( <div> <ThemeContext.Provider value={theme}> <ThemedButton onClick={toggleTheme} /> <ThemeContext.Consumer> {(theme) => ( <div style={{ height: 300, width: 300, backgroundColor: theme.background, }} ></div> )} </ThemeContext.Consumer> </ThemeContext.Provider> <ThemedButton /> </div> ); }
|
Portals
- Portal은 부모 컴포넌트의 DOM 계층 구조 바깥에 있는 DOM 노드로 자식을 렌더링하는 최고의 방법을 제공
Portal을 사용하지 않았을 경우
1 2 3 4 5 6 7 8 9 10 11 12 13 14
| import React from 'react'; import ThankyouDialog from './ThankyouDialog';
export default function Portal() { return ( <div onClick={() => console.log('div')}> <ThankyouDialog /> <div style={{ position: 'absolute' }}> <button>하하하</button> </div> </div> ); }
|
- ThankyouDialog 컴포넌트보다 아래에 적힌 버튼은 모달창이 떠도 가려지지 않는다
Portal을 사용했을 경우
1 2 3 4 5
| <body> <div id="root"></div> <div id="portal"></div> </body>
|
- index.html에 root 아래 portal 생성
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20
| import React from 'react'; import { createPortal } from 'react-dom'; import ThankyouDialog from './ThankyouDialog'; const Portals = (props) => { return createPortal(props.children, document.getElementById('portal')); }; export default function Portal() { return ( <div onClick={() => console.log('div')}> <Portals> <ThankyouDialog /> </Portals>
<div style={{ position: 'absolute' }}> <button>하하하</button> </div> </div> ); }
|
- Portals 컴포넌트를 새로 생성 createPortal을 return 한다
- ThankyouDialog를 Portals로 감싸준다
PropTypes
Props의 타입을 확인하기 위한 도구
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24
| import React from 'react'; import propTypes from 'prop-types';
const PropComponent = (props) => { return <div>{props.name}</div>; };
PropComponent.defaultProps = { name: 'Jimmy', age: '8', };
PropComponent.propTypes = { name: propTypes.string, age: propTypes.number.isRequired, };
export default function PropTypesCom() { return ( <div> <PropComponent /> </div> ); }
|
age의 propTypes는 number인데 ‘8’처럼 문자로 지정하면 에러가 뜬다
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34
| import React from 'react'; import propTypes from 'prop-types';
const PropComponent = (props) => { return <div>{props.name}</div>; };
PropComponent.defaultProps = { name: 'Jimmy', age: 8, };
PropComponent.propTypes = { name: propTypes.string, age: function (props, propName, componentName) { if (!/7/.test(props[propName])) { return new Error( 'Invalid prop `' + propName + '` supplied to' + ' `' + componentName + '`. Validation failed.' ); } }, }; export default function PropTypesCom() { return ( <div> <PropComponent /> </div> ); }
|
함수를 이용해 체크를 해줄수도 있다