Having Tech Interview?
3600 Tech Interview Questions. Answered.
You can download the PDF and Epub version of this repository from the latest run on the actions tab.
- ๐ ๋ณธ ๋ฌธ์๋ sudheerj์ reactjs-interview-questions์ ๋ฒ์ญ๋ณธ์ ๋๋ค.
- โญ ์ด ํ๋ก์ ํธ๊ฐ ๋ง์์ ๋์ จ๋ค๋ฉด STAR๋ฅผ ๋๋ฌ์ฃผ์ธ์.
- โ๏ธ ํ ๋ฆฌํ์คํธ๋ ์ธ์ ๋ ํ์์ ๋๋ค.
- ๐ vuejs-interview-questions-korean๋ฅผ ์ฐธ๊ณ ๋ฅผ ํด์ ์์ฑํ์์ต๋๋ค.
-
React๋ ๋จ์ผ ํ์ด์ง ์ดํ๋ฆฌ์ผ์ด์ ์ผ๋ก UI(user interface)๋ฅผ ๋ง๋๋๋ฐ ์ฌ์ฉ๋๋ ์คํ-์์ค ํ๋ก ํธ์๋ JavaScript ๋ผ์ด๋ธ๋ฌ๋ฆฌ ๋ก ์น๊ณผ ๋ชจ๋ฐ์ผ ์ฑ์์ view layer๋ฅผ ๋ค๋ฃจ๋๋ฐ ์ฌ์ฉ๋๋ค. React๋ Facebook์์ ์ํํธ์จ์ด ์์ง๋์ด๋ก ์๋ Jordan Walke๊ฐ ๋ง๋ค์๋ค. React๋ 2011๋ Facebook's News Feed, 2012๋ Instagram์์ ์ฒ์ ์ ๋ณด์๋ค.
-
React์ ์ฃผ์ ํน์ง์ :
- ์ค์ ๋(RealDOM)์ ์กฐ์ํ๋ ๊ฒ์ ๋น์ฉ์ด ํฌ๋ค๋ ๊ฒ์ ๊ณ ๋ คํ์ฌ ์ค์ ๋ ๋์ ์ ๊ฐ์๋(VirtualDOM) ์ ์ฌ์ฉํ๋ค.
- ์๋ฒ-์ฌ์ด๋ ๋ ๋๋ง(server-side rendering) ๋ฅผ ์ง์ํ๋ค.
- ๋จ๋ฐฉํฅ ๋ฐ์ดํฐ ํ๋ฆ ๋๋ ๋ฐ์ดํฐ ๋ฐ์ธ๋ฉ์ ๋ฐ๋ฅธ๋ค.
- ํ๋ฉด ๊ฐ๋ฐ์ ์ํด ์ฌ์ฌ์ฉ ๊ฐ๋ฅํ(reusable)/๊ตฌ์ฑ ๊ฐ๋ฅํ(composable) UI ์ปดํฌ๋ํธ๋ฅผ ์ฌ์ฉํ๋ค.
-
JSX ๋ ECMAScript๋ฅผ ์ํ XML-์ฒ๋ผ ์๊ธด ๊ตฌ๋ฌธ ํ์ฅ์(JavaScript XML ์ ์ฝ์ด)์ด๋ค. ๊ธฐ๋ณธ์ ์ผ๋ก
React.createElement()
ํจ์๋ฅผ ์ํ syntactic sugar, JavaScript์ ํํ๋ ฅ๊ณผ HTML๊ฐ์ ํ ํ๋ฆฟ ๋ฌธ๋ฒ์ ์ ๊ณตํ๋ค.์๋ ์์ ์์
<h1>
ํ๊ทธ ์์ ํ ์คํธ๋ render ํจ์์ JavaScript ํจ์๋ก ๋ฐํ๋๋ค.class App extends React.Component { render() { return( <div> <h1>{'Welcome to React world!'}</h1> </div> ) } }
-
Element ๋ DOM ๋ ธ๋๋ ๋ค๋ฅธ ์ปดํฌ๋ํธ๋ค ๊ด์ ์์ ํ๋ฉด์ ๋ณด์ด๊ธฐ ์ํ๋ ๊ฑธ ๋ฌ์ฌํ ์ผ๋ฐ ๊ฐ์ฒด์ด๋ค. Elements ๋ props์ ์๋ ๋ค๋ฅธ Elements ๋ฅผ ํฌํจํ ์ ์๋ค. React element๋ฅผ ๋ง๋๋ ๊ฒ์ ์ ๋ ดํ๋ค. ์ผ๋จ element๊ฐ ๋ง๋ค์ด์ง๋ฉด ์ ๋ ๋ณ๊ฒฝ๋์ง ์๋๋ค.
React Element ๊ฐ์ฒด ํํ์ ๋ค์๊ณผ ๊ฐ๋ค. :
const element = React.createElement( 'div', {id: 'login-btn'}, 'Login' )
์์
React.createElement()
ํจ์๋ ๊ฐ์ฒด๋ฅผ ๋ฐํํ๋ค. :{ type: 'div', props: { children: 'Login', id: 'login-btn' } }
๋ง์ง๋ง์ผ๋ก
ReactDOM.render()
๋ฅผ ์ฌ์ฉํด์ DOM์ผ๋ก ๋ ๋๋งํ๋ค. :<div id='login-btn'>Login</div>
๋ฐ๋ฉด์ component ๋ ์ฌ๋ฌ ๋ค๋ฅธ ๋ฐฉ๋ฒ์ผ๋ก ์ ์ธ๋ ์ ์๋ค.
render()
๋ฉ์๋๋ฅผ ํฌํจํ ํด๋์ค๊ฐ ๋ ์ ์๋ค. ๋๋ ๋จ์ํ๊ฒ ํจ์๋ก ์ ์๋ ์ ์๋ค. ๋ ๊ฒฝ์ฐ ๋ชจ๋, ์ ๋ ฅ์ผ๋ก props๋ฅผ ๊ฐ์ ธ์ค๊ณ , ์ถ๋ ฅ์ผ๋ก JSX tree๋ฅผ returnํ๋ค.const Button = ({ onLogin }) => <div id={'login-btn'} onClick={onLogin}>Login</div>
JSX๋
React.createElement()
ํจ์ ํธ๋ฆฌ๋ก ๋ณํ๋๋ค. :const Button = ({ onLogin }) => React.createElement( 'div', { id: 'login-btn', onClick: onLogin }, 'Login' )
-
์ปดํฌ๋ํธ๋ฅผ ๋ง๋๋ ๋ฐฉ๋ฒ์ 2๊ฐ์ง๊ฐ ์๋ค.
-
Function Components: ์ปดํฌ๋ํธ๋ฅผ ๋ง๋๋ ๊ฐ์ฅ ๊ฐ๋จํ ๋ฐฉ๋ฒ์ด๋ค. ์์ JavaScript functions ๋ก ์ฒซ๋ฒ์งธ ์ธ์๋ก๋ props ๊ฐ์ฒด๋ฅผ ๋ฐ๊ณ React elements๋ฅผ ๋ฐํํ๋ค. :
function Greeting({ message }) { return <h1>{`Hello, ${message}`}</h1>โจ }
-
Class Components: ES6์ class๋ฅผ ์ฌ์ฉํด์ ์ปดํฌ๋ํธ๋ฅผ ๋ง๋ค ์ ์๋ค. ์์ ํจ์๋ ๋ค์๊ณผ ๊ฐ์ด ์์ฑ๋ ์ ์๋ค. :
class Greeting extends React.Component { render() { return <h1>{`Hello, ${this.props.message}`}</h1> } }
-
-
๋ง์ฝ, ์ปดํฌ๋ํธ๊ฐ
state๋ lifecycle methods
๊ฐ ํ์ํ๋ค๋ฉด class ์ปดํฌ๋ํธ๋ฅผ ์ฌ์ฉํ๊ณ ์๋๋ผ๋ฉด function ์ปดํฌ๋ํธ๋ฅผ ์ฌ์ฉํ๋ค. -
React.PureComponent
๋shouldComponentUpdate()
๋ฉ์๋๋ฅผ ์ ์ดํ๋ ๊ฒ์ ์ ์ธํ๋ฉด *React.Component
*์ ๋ค๋ฅด์ง ์๋ค. props๋ state๊ฐ ๋ณ๊ฒฝ๋๋ฉด PureComponent ๋ props์ state ์ ๋ํด์ ์์ ๋น๊ต๋ฅผ ์ํํ๋ค. ๋ฐ๋ฉด์ Component ๋ ํ์ฌ์ props์ state์ ๋ํด ๋น๊ต๋ฅผ ํ์ง ์๋๋ค. ๋ฐ๋ผ์shouldComponentUpdate
๊ฐ ํธ์ถ๋ ๋๋ง๋ค ๋ฆฌ๋ ๋๋ง๋๋ค. -
์ปดํฌ๋ํธ์ State๋ ์ปดํฌ๋ํธ์ ๋ณ๊ฒฝ ๋ ์ ์๋ ์ ๋ณด๋ฅผ ๋ณด์ ํ๋ ๊ฐ์ฒด์ด๋ค. ๊ฐ๋ฅํ ํ ๊ฐ๋จํ๊ฒ ์ํ๋ฅผ ๋ง๋ค๊ณ stateful ์ปดํฌ๋ํธ์ ์๋ฅผ ์ต์ํํด์ผ ํ๋ค. message state๋ฅผ ๊ฐ์ง User ์ปดํฌ๋ํธ๋ฅผ ๋ง๋ค์ด๋ณด์.
class User extends React.Component { constructor(props) { super(props) this.state = { message: 'Welcome to React world' } } render() { return ( <div> <h1>{this.state.message}</h1> </div> ) } }
state๋ props์ ๋น์ทํ์ง๋ง private ํ๋ฉฐ ์ปดํฌ๋ํธ์ ์ํด ์ ์ด๋๋ค. ์ฆ, ์ํ๋ ์ด๋ฅผ ๊ฐ์ง๊ณ ์๊ฑฐ๋ ์ค์ ํ ์ ์๋ ์ปดํฌ๋ํธ ์ด์ธ์๋ ์ ๊ทผํ ์ ์๋ค.
-
Props๋ ์ปดํฌ๋ํธ์ ๋ํ ์ ๋ ฅ์ด๋ค. Props๋ HTML ํ๊ทธ ์์ฑ๊ณผ ๊ฐ์ ์๋ช ๊ท์น์ ์ฌ์ฉํ์ฌ ์ปดํฌ๋ํธ์ ์ ๋ฌ๋๋ ๋จ์ผ ๊ฐ ํน์ ๊ฐ์ฒด๋ค. props๋ ๋ถ๋ชจ ์ปดํฌ๋ํธ์์ ์์ ์ปดํฌ๋ํธ๋ก ์ ๋ฌ๋๋ค.
React์์ props์ ์ฃผ๋ชฉ์ ์ ๋ค์๊ณผ ๊ฐ์ ์ปดํฌ๋ํธ์ ๊ธฐ๋ฅ๋ค์ ์ ๊ณตํ๋ ๊ฒ์ด๋ค.
- ์ปค์คํ ๋ฐ์ดํฐ๋ฅผ ์ปดํฌ๋ํธ๋ก ์ ๋ฌํ๋ค.
- state์ ๋ณ๊ฒฝ์ ์ผ์ผํจ๋ค.
- ์ปดํฌ๋ํธ์
render()
๋ฉ์๋ ๋ด์์this.props.reactProp
๋ฅผ ํตํด ์ฌ์ฉํ๋ค.
์๋ฅผ ๋ค์ด
reactProp
์์๋ฅผ ๊ฐ์ง ์๋ฆฌ๋จผํธ๋ฅผ ๋ง๋ค์ด๋ณด์.<Element reactProp={'1'} />
์ด
reactProp
(๋๋ ์ฌ๋ฌ๋ถ์ด ๋ง๋ ๊ฒ์ ๋ฌด์์ด๋ ) React๋ฅผ ์ฌ์ฉํ์ฌ ์์ฑ๋ ๋ชจ๋ ์ปดํฌ๋ํธ์ ์๋ ์กด์ฌํ๋ props ๊ฐ์ฒด์ ์์ฑ์ด ๋๋ค.props.reactProp
-
props์ state๋ ๋ชจ๋ ์์ ์๋ฐ์คํฌ๋ฆฝํธ ๊ฐ์ฒด๋ค. ๋ ๋ค ๋ ๋๋ง ๊ฒฐ๊ณผ์ ์ํฅ์ ์ฃผ๋ ์ ๋ณด๋ฅผ ๊ฐ์ง๊ณ ์์ง๋ง, ์ปดํฌ๋ํธ์ ๊ด๋ จ๋ ๊ธฐ๋ฅ ๋ฉด์์ ๋ค๋ฅด๋ค. props๋ ํจ์์ ๋งค๊ฐ๋ณ์์ ๋น์ทํ๊ฒ ์ปดํฌ๋ํธ๋ก ์ ๋ฌ๋๋ ๋ฐ๋ฉด, state๋ ํจ์ ๋ด์์ ์ ์ธ๋ ๋ณ์์ ์ ์ฌํ๊ฒ ์ปดํฌ๋ํธ ๋ด์์ ๊ด๋ฆฌ๋๋ค.
-
ํน์๋ผ๋ state๋ฅผ ์ง์ ์ ์ผ๋ก ์ ๋ฐ์ดํธ๋ฅผ ํ๊ฒ ๋๋ฉด ์ปดํฌ๋ํธ๋ re-render๋ฅผ ํ์ง ์๋๋ค.
//Wrong this.state.message = 'Hello world'
๋์ ์
setState()
๋ฉ์๋๋ฅผ ์ฌ์ฉํ๋ค. ์ด ๋ฉ์๋๋ ์ปดํฌ๋ํธ์ state ๊ฐ์ฒด์ ๋ํ ์ ๋ฐ์ดํธ๋ฅผ ์์ฝํ๋ค. state๊ฐ ๋ฐ๋๊ฒ ๋๋ฉด, ์ปดํฌ๋ํธ๋ re-rendering์ผ๋ก ์๋ต์ ํ๋ค.//Correct this.setState({ message: 'Hello World' })
Note: constructor์์๋ ์ต์ Javascript์ class ์ ์ธ ๊ตฌ๋ฌธ์ ์ฌ์ฉํด์ state ๊ฐ์ฒด์ ์ง์ ํ ๋นํ ์ ์๋ค.
-
callback ํจ์๋ setState๊ฐ ๋๋๊ณ ์ปดํฌ๋ํธ๊ฐ render ๋์์ ๋ ์๋ํ๋ค.
setState()
๋ ๋น๋๊ธฐ์์ด์ฌ์ callback ํจ์๋ ๋ชจ๋ ์์ ํ ์ฌ์ฉ๋๋ค.Note: ์ด callback ํจ์๋ณด๋ค๋ lifecycle ๋ฉ์๋๋ฅผ ์ฌ์ฉํ๋ ๊ฒ์ด ์ข๋ค.
setState({ name: 'John' }, () => console.log('The name has updated and component re-rendered'))
-
- HTML์์, ์ด๋ฒคํธ ์ด๋ฆ์ ์๋ฌธ์์ฌ์ผ ํ๋ค:
<button onclick='activateLasers()'>
๋ฐ๋ฉด์ React์์๋ camelCase ๊ท์น์ ๋ฐ๋ฅธ๋ค:
<button onClick={activateLasers}>
- HTML์์, ๊ธฐ๋ณธ ๋์ ๋ฐฉ์ง๋ฅผ ์ํด
false
๋ฅผ ๋ฐํํ ์ ์๋ค:
<a href='#' onclick='console.log("The link was clicked."); return false;' />
๋ฐ๋ฉด์ React์์๋
preventDefault()
๋ฅผ ๋ช ์์ ์ผ๋ก ํธ์ถํด์ผ ํ๋ค:function handleClick(event) { event.preventDefault() console.log('The link was clicked.') }
-
์ด๋ฅผ ์ด๋ฃจ๊ธฐ ์ํ 3๊ฐ์ง ๋ฐฉ๋ฒ์ด ์๋ค.
- ์์ฑ์์์ ๋ฐ์ธ๋ฉ: JavaScript ํด๋์ค์์, ๋ฉ์๋๋ ๊ธฐ๋ณธ์ ์ผ๋ก ๋ฐ์ธ๋ฉ ๋์ง ์๋๋ค. ํด๋์ค ๋ฉ์๋๋ก ์ ์๋ React ์ด๋ฒคํธ ํธ๋ค๋ฌ์๊ฒ์๋ ๋๊ฐ์ ๋ฌธ์ ๊ฐ ์ ์ฉ๋๋ค. ์ผ๋ฐ์ ์ผ๋ก ์ฐ๋ฆฌ๋ ์์ฑ์ ์์์ ๋ฐ์ธ๋๋ฅผ ํ๋ค.
class Component extends React.Componenet { constructor(props) { super(props) this.handleClick = this.handleClick.bind(this) } handleClick() { // ... } }
- Public ํด๋์ค ํ๋ ๊ตฌ๋ฌธ: ๋ฐ์ธ๋ ์ ๊ทผ๋ฒ์ด ์ซ๋ค๋ฉด ์ฝ๋ฐฑ์ ์ฌ๋ฐ๋ฅด๊ฒ ๋ฐ์ธ๋ํ๊ธฐ ์ํด public ํด๋์ค ํ๋ ๊ตฌ๋ฌธ ์ ์ฌ์ฉํ ์ ์๋ค.
handleClick = () => { console.log('this is:', this) }
<button onClick={this.handleClick}> {'Click me'} </button>
- ์ฝ๋ฐฑ ์์์ ํ์ดํ ํจ์: ์ฝ๋ฐฑ ์์์ ์ง์ ํ์ดํ ํจ์๋ฅผ ์ฌ์ฉํ ์ ์๋ค.
<button onClick={(event) => this.handleClick(event)}> {'Click me'} </button>
Note: ์ฝ๋ฐฑ์ด ์์ ์ปดํฌ๋ํธ์ prop์ผ๋ก ์ ๋ฌ์ด ๋๋ฉด, ํด๋น ์ปดํฌ๋ํธ๋ ์ถ๊ฐ ๋ฆฌ๋ ๋๋ง์ ์ํํ ์ ์๋ค. ์ด ๊ฒฝ์ฐ์, ์ฑ๋ฅ์ ๊ณ ๋ คํ์ฌ
.bind()
๋๋ public ํด๋์ค ํ๋ ๊ตฌ๋ฌธ ์ ๊ทผ๋ฒ์ ์ฌ์ฉํ๋ ๊ฒ์ด ์ข๋ค. -
ํ์ดํ ํจ์๋ฅผ ์ฌ์ฉํด์ ์ด๋ฒคํธ ํธ๋ค๋ฌ๋ฅผ ๊ฐ์ธ๊ณ ๋งค๊ฐ๋ณ์๋ฅผ ์ ๋ฌํ ์ ์๋ค.
<button onClick={() => this.handleClick(id)} />
.bind
๋ฅผ ํธ์ถํ๋ ๊ฒ๊ณผ ๋์ผํ๋ค:<button onClick={this.handleClick.bind(this, id)} />
Apart from these two approaches, you can also pass arguments to a function which is defined as array function
<button onClick={this.handleClick(id)} /> handleClick = (id) => () => { console.log("Hello, your ticket number is", id) };
-
SyntheticEvent
๋ ๋ธ๋ผ์ฐ์ ์ ๊ธฐ๋ณธ ์ด๋ฒคํธ๋ฅผ ๊ฐ์ผ cross-browser wrapper์ด๋ค.stopPropagation()
๊ณผpreventDefault()
๋ฅผ ํฌํจํ API๋ก, ๋ธ๋ผ์ฐ์ ์ ๊ธฐ๋ณธ ์ด๋ฒคํธ์ ๋์ผํ์ง๋ง, ๋ชจ๋ ๋ธ๋ผ์ฐ์ ์์ ๋์ผํ๊ฒ ์๋ํ๋ค๋ ์ ์ด ๋ค๋ฅด๋ค. -
JS์์ ์กฐ๊ฑด๋ถ๋ก ์์ ํํํ๊ธฐ ์ํด์ if ๋ฌธ ์ด๋ ์ผํญ ํํ์ ์ ์ฌ์ฉํ ์ ์๋ค. ์ด๋ฌํ ์ ๊ทผ๋ฒ ์ธ์๋, JS ๋ ผ๋ฆฌ์ฐ์ฐ์์ธ
&&
๊ฐ ์๋ ์ค๊ดํธ๋ก ๊ฐ์ผ ํํ์์ JSX์ ํฌํจํ ์๋ ์๋ค.<h1>Hello!</h1> { messages.length > 0 && !isLogin? <h2> You have {messages.length} unread messages. </h2> : <h2> You don't have unread messages. </h2> }
-
key
๋ elements ๋ฐฐ์ด์ ๋ง๋ค ๋ ํฌํจ์์ผ์ผ ํ๋ ํน์ ๋ฌธ์์ด ์์ฑ์ด๋ค. Keys๋ React๊ฐ ๋ณ๊ฒฝ, ์ถ๊ฐ, ์ ๊ฑฐ๋ ํญ๋ชฉ์ ์๋ณํ๋ ๋ฐ ๋์์ ์ค๋ค.์ฐ๋ฆฌ๋ ๋๊ฐ ๋ฐ์ดํฐ์์ ID๋ฅผ keys๋ก ์ฌ์ฉํ๋ค.
const todoItems = todos.map((todo) => <li key={todo.id}> {todo.text} </li> )
๋ ๋๋ง๋ ํญ๋ชฉ ์ค ์์ ์ ์ธ ID๊ฐ ์์ ๋, ๋ง์ง๋ง ์๋จ์ผ๋ก ํญ๋ชฉ์ index๋ฅผ key๋ก ์ฌ์ฉํ ์ ์๋ค.
const todoItems = todos.map((todo, index) => <li key={index}> {todo.text} </li> )
Note:
- ํญ๋ชฉ์ ์์๊ฐ ๋ณ๊ฒฝ๋ ์ ์๋ ๊ฒฝ์ฐ index๋ฅผ keys๋ก ์ฌ์ฉํ๋ ๊ฒ์ ์ถ์ฒํ์ง ์๋๋ค. ์ด๋ ์ฑ๋ฅ ์ ํ์ ์ปดํฌ๋ํธ state์ ๋ฌธ์ ๋ฅผ ๋ฐ์์ํฌ ์ ์๋ค.
- ๋ฆฌ์คํธ ํญ๋ชฉ์ ๋ณ๋์ ์ปดํฌ๋ํธ๋ก ์ถ์ถํ๋ ๊ฒฝ์ฐ
li
ํ๊ทธ ๋์ ์ ๋ฆฌ์คํธ ์ปดํฌ๋ํธ์ keys๋ฅผ ์ ์ฉํด๋ผ. - ๋ฆฌ์คํธ ํญ๋ชฉ์
key
prop๊ฐ ์์ผ๋ฉด console์ ๊ฒฝ๊ณ ๋ฉ์์ง๊ฐ ํ์๋ ๊ฒ์ด๋ค.
-
ref๋ ์๋ฆฌ๋จผํธ์ ๋ํ ์ฐธ์กฐ๋ฅผ ๋ฐํํ๋ ๋ฐ ์ฌ์ฉ๋๋ค. ๋๋ถ๋ถ์ ๊ฒฝ์ฐ์์ ํผํ๋ ๊ฒ์ด ์ข์ง๋ง, DOM ์๋ฆฌ๋จผํธ๋ ์ปดํฌ๋ํธ์ ์ธ์คํด์ค์ ์ง์ ์ ๊ทผํ๋ ๊ฒฝ์ฐ ์ ์ฉํ ์ ์๋ค.
-
refs๋ฅผ ์์ฑํ๋ ๋ฐฉ๋ฒ์๋ 2๊ฐ์ง ๋ฐฉ๋ฒ์ด ์๋ค.
- ๋ค์์ ์ต๊ทผ์ ์ถ๊ฐ๋ ๋ฐฉ๋ฒ์ด๋ค. Refs๋
React.createRef()
๋ฉ์๋๋ฅผ ํตํด ์์ฑ๋๋ฉฐref
์์ฑ์ ํตํด React ์๋ฆฌ๋จผํธ์ ์ ์ฉ๋๋ค. ์๋ฆฌ๋จผํธ ์ ์ฒด์์ refs๋ฅผ ์ฌ์ฉํ๋ ค๋ฉด ์์ฑ์ ๋ด์ ์ธ์คํด์ค ์์ฑ์ ref๋ฅผ ํ ๋นํ๋ฉด ๋๋ค.
class MyComponent extends React.Component { constructor(props) { super(props) this.myRef = React.createRef() } render() { return <div ref={this.myRef} /> } }
- React ๋ฒ์ ์ ๊ด๊ณ์์ด ref ์ฝ๋ฐฑ ์ ๊ทผ๋ฒ์ ์ฌ์ฉํ ์๋ ์๋ค. ์๋ฅผ ๋ค์ด, ๊ฒ์์ฐฝ ์ปดํฌ๋ํธ์ ์ ๋ ฅ ์๋ฆฌ๋จผํธ์์๋ ๋ค์๊ณผ ๊ฐ์ด ์ ๊ทผํ๋ค.
class SearchBar extends Component { constructor(props) { super(props); this.txtSearch = null; this.state = { term: '' }; this.setInputSearchRef = e => { this.txtSearch = e; } } onInputChange(event) { this.setState({ term: this.txtSearch.value }); } render() { return ( <input value={this.state.term} onChange={this.onInputChange.bind(this)} ref={this.setInputSearchRef} /> ); } }
ํด๋ก์ (closures) ๋ฅผ ์ฌ์ฉํ์ฌ ํจ์ํ ์ปดํฌ๋ํธ์์ refs๋ฅผ ์ฌ์ฉํ ์๋ ์๋ค. ์ฐธ๊ณ : ๊ถ์ฅํ์ง ์์ง๋ง, ์ธ๋ผ์ธ ref ์ฝ๋ฐฑ์ ์ด์ฉํ ์๋ ์๋ค.
- ๋ค์์ ์ต๊ทผ์ ์ถ๊ฐ๋ ๋ฐฉ๋ฒ์ด๋ค. Refs๋
-
Ref ์ ๋ฌ(Ref forwarding) ํน์ ์ปดํฌ๋ํธ์์ ref๋ฅผ ๋ฐ์ ์์์๊ฒ ์ ๋ฌํ๋ ๊ธฐ๋ฅ์ด๋ค.
const ButtonElement = React.forwardRef((props, ref) => ( <button ref={ref} className="CustomButton"> {props.children} </button> )); // Create ref to the DOM button: const ref = React.createRef(); <ButtonElement ref={ref}>{'Forward Ref'}</ButtonElement>
-
findDOMNode()
API ์์ callback refs๋ฅผ ์ฌ์ฉํ๋ ๊ฒ์ด ์ข๋ค.findDOMNode()
์ด ์ถํ React์์์ ๊ฐ์ ์ฌํญ์ ๋ฐฉํดํ๊ธฐ ๋๋ฌธ์ด๋ค.findDOMNode
๋ฅผ ์ฌ์ฉํ๋ legacy ์ ๊ทผ๋ฒ์ ๋ค์๊ณผ ๊ฐ๋ค.class MyComponent extends Component { componentDidMount() { findDOMNode(this).scrollIntoView() } render() { return <div /> } }
๊ถ์ฅ๋๋ ์ ๊ทผ๋ฒ์ ๋ค์๊ณผ ๊ฐ๋ค.
class MyComponent extends Component { constructor(props){ super(props); this.node = createRef(); } componentDidMount() { this.node.current.scrollIntoView(); } render() { return <div ref={this.node} /> } }
-
React๋ฅผ ์ฌ์ฉํด๋ณด๊ธฐ ์ ์ด๋ผ๋ฉด, ์์ API์์
ref={'textInput'}
๊ณผ ๊ฐ์ref
์์ฑ์ด ๋ฌธ์์ด์ธ ๊ฒ๊ณผ DOM node๊ฐthis.refs.textInput
๊ณผ ๊ฐ์ด ์ก์ธ์ค ๋๋ ๊ฒ์ ์ต์ํ ๊ฒ์ด๋ค. String Refs์ ๋ฌธ์ ๊ฐ ์์ด, legacy๋ก ๊ฐ์ฃผํ๊ธฐ ๋๋ฌธ์ ์ฌ์ฉํ์ง ์๊ธฐ๋ฅผ ๋ฐ๋๋ค. String Refs๋ React v16์ ์ ๊ฑฐ๋์๋ค.- React๊ฐ ํ์ฌ ์คํ ์ค์ธ ์ปดํฌ๋ํธ๋ฅผ ์ถ์ ํ๋๋ก ๊ฐ์ ๋๋ค. ์ด๋ react ๋ชจ๋์ stateful ํ๊ฒ ๋ง๋ค๊ณ , react ๋ชจ๋์ด ๋ฒ๋ค์ ๋ณต์ ๋ ๋ ์ด์ํ ์ค๋ฅ๋ฅผ ์ ๋ฐํ๊ธฐ ๋๋ฌธ์ ๋ฌธ์ ๊ฐ ๋๋ค.
- composable ํ์ง ์๋ค. โ ๋ผ์ด๋ธ๋ฌ๋ฆฌ๊ฐ ์ ๋ฌ๋ ์์์ ref๋ฅผ ๋ฃ์ผ๋ฉด, ์ฌ์ฉ์๋ ๋ค๋ฅธ ref๋ฅผ ์ถ๊ฐํ ์ ์๋ค. Callback ref๋ ์๋ฒฝํ๊ฒ ๊ตฌ์ฑ์ด ๊ฐ๋ฅํ๋ค.
- Flow์ ๊ฐ์ ์ ์ ๋ถ์์์๋ ์๋ํ์ง ์๋๋ค. Flow๋ ํ๋ ์์ํฌ๊ฐ String Refs๋ฅผ
this.refs
์ ํ์ํ๋๋ก ํ๋ ๋ง๋ฒ๊ณผ ๊ทธ๊ฒ์ ํ์ (๋ค๋ฅผ ์ ์์)์ ์ถ์ธกํ ์ ์๋ค. Callback ref๋ ์ ์ ๋ถ์์ ์น์ํฉ๋๋ค.. - ๋๋ถ๋ถ์ ์ฌ๋์ด ์๊ฐํ๋ "render callback" ํจํด์ผ๋ก ์๋ํ์ง ์๋๋ค. (์).
<DataGrid renderRow={this.renderRow} />
)class MyComponent extends Component { renderRow = (index) => { // This won't work. Ref will get attached to DataTable rather than MyComponent: return <input ref={'input-' + index} />; // This would work though! Callback refs are awesome. return <input ref={input => this['input-' + index] = input} />; } render() { return <DataTable data={this.props.data} renderRow={this.renderRow} /> } }
-
Virtual DOM (VDOM)๋ ์ค์ DOM์ ์ธ-๋ฉ๋ชจ๋ฆฌ ํํ์ด๋ค. UI ํํ์ ๋ฉ๋ชจ๋ฆฌ์ ์ ์ง๋๊ณ "์ค์ " DOM๊ณผ ๋๊ธฐํ๋๋ค. ์ด๋ ๋ ๋ ํจ์์ ํธ์ถ๊ณผ ํ๋ฉด์ elements๋ฅผ ํํํ๋ ์ฌ์ด์ ๋ฐ์ํ๋ ๋จ๊ณ์ด๋ค. ์ด ์ ์ฒด ํ๋ก์ธ์ค๋ ์กฐ์ ์ด๋ผ๊ณ ํ๋ค.
-
Virtual DOM๋ ์ธ ๊ฐ์ง ๊ฐ๋จํ ๋จ๊ณ๋ก ์๋ํ๋ค.
-
Shadow DOM ์ฃผ๋ก ์น ์ปดํฌ๋ํธ์์ ๋ณ์์ CSS์ ๋ฒ์ ์ง์ ์ ์ํด ๋์์ธ๋ ๋ธ๋ผ์ฐ์ ๊ธฐ์ ์ด๋ค. Virtual DOM๋ ๋ธ๋ผ์ฐ์ API๋ฅผ ๊ธฐ๋ฐ์ผ๋ก Javascript ๋ผ์ด๋ธ๋ฌ๋ฆฌ๋ก ๊ตฌํ๋ ๊ฐ๋ ์ด๋ค.
-
Fiber๋ React v16์์ ์๋ก์ด ์กฐ์ ๋ ์์ง ๋๋ ํต์ฌ ์๊ณ ๋ฆฌ์ฆ์ ์ฌ๊ตฌํ์ด๋ค. React Fiber์ ๋ชฉํ๋ ์ ๋๋ฉ์ด์ , ๋ ์ด์์, ์ ์ค์ฒ, ์์ ์ผ์์ค์ง, ์ค๋จ, ์ฌ์ฌ์ฉ ๊ฐ์ ์์ญ์ ๋ํ ์ ํฉ์ฑ์ ๋์ด๊ณ ๋ค์ํ ์ ํ์ ์ ๋ฐ์ดํธ์ ์ฐ์ ์์๋ฅผ ์ ํ๋ ๊ฒ์ด๋ค.
-
React Fiber์ ๋ชฉํ๋ ์ ๋๋ฉ์ด์ , ๋ ์ด์์, ์ ์ค์ฒ, ์์ ์ผ์์ค์ง, ์ค๋จ, ์ฌ์ฌ์ฉ ๊ฐ์ ์์ญ์ ๋ํ ์ ํฉ์ฑ์ ๋์ด๋ ๊ฒ์ด๋ค. ์ฃผ์ ๊ธฐ๋ฅ์ incremental rendering์ผ๋ก ๋ ๋๋ง ์์ ์ ์ฒญํฌ(chunk)๋ก ๋ถํ ํ๊ณ ์ฌ๋ฌ ํ๋ ์์ ๊ฑธ์ณ ํผ์น๋ ๊ธฐ๋ฅ์ด๋ค.
-
์ฌ์ฉ์๊ฐ ์ ๋ ฅํ ๋ค ํผ์ ์ ๋ ฅ์ฐฝ์ ์ ์ดํ๋ component๋ฅผ Controlled Component๋ผ๊ณ ํ๋ค. ์ฆ ๋ชจ๋ ์ํ ๋ณ์ด์๋ ๊ด๋ จ๋ ํธ๋ค๋ฌ ํจ์๊ฐ ์๋ค.
์๋ฅผ ๋ค์ด, ๋ชจ๋ ์ด๋ฆ์ ๋๋ฌธ์๋ก ์์ฑํ๊ธฐ ์ํด์, ์๋์ ๊ฐ์ handleChange๋ฅผ ์ฌ์ฉํ๋ค.
handleChange(event) { this.setState({value: event.target.value.toUpperCase()}) }
-
The Uncontrolled Components๋ ๋ด๋ถ์ ์ผ๋ก ์์ ์ ์ํ๋ฅผ ์ ์ฅํ๋ ๊ฒ์ผ๋ก, ํ์ฌ์ ๊ฐ์ ์ฐพ๊ธฐ ์ํด ํ์ํ๋ค๋ฉด ref๋ฅผ ์ฌ์ฉํ์ฌ DOM์์ ์ฐพ์์จ๋ค. ์ด๊ฒ์ ์ ํต์ ์ธ HTML๊ณผ ๋ ๋น์ทํ๋ค.
์๋์ UserProfile component์์,
์ด๋ฆ
์ ๋ ฅ์ ref๋ฅผ ์ฌ์ฉํด์ ์ก์ธ์คํ๋ค.class UserProfile extends React.Component { constructor(props) { super(props) this.handleSubmit = this.handleSubmit.bind(this) this.input = React.createRef() } handleSubmit(event) { alert('A name was submitted: ' + this.input.current.value) event.preventDefault() } render() { return ( <form onSubmit={this.handleSubmit}> <label> {'Name:'} <input type="text" ref={this.input} /> </label> <input type="submit" value="Submit" /> </form> ); } }
๋๋ถ๋ถ์ ๊ฒฝ์ฐ ์์์ ๊ตฌํํ๋ ๋ฐ controlled components๋ฅผ ์ฌ์ฉํ๋ ๊ฒ์ ์ถ์ฒํ๋ค.
-
JSX elements๋
React.createElement()
ํจ์๋ก ์ฎ๊ฒจ์ ธ์ UI์ ๊ฐ์ฒด ํํ์ ์ฌ์ฉ๋๋ React elements๋ฅผ ๋ง๋ ๋ค. ๋ฐ๋ฉด์cloneElement
์ element๋ฅผ ๋ณต์ฌํ๊ณ ์๋ก์ด props๋ก ์ ๋ฌํ๋ ๋ฐ ์ฌ์ฉ๋๋ค. -
์ฌ๋ฌ ์ปดํฌ๋ํธ๊ฐ ๋์ผํ ๋ณ๊ฒฝ ๋ฐ์ดํฐ๋ฅผ ๊ณต์ ํด์ผ ํ๋ ๊ฒฝ์ฐ ๊ฐ์ฅ ๊ฐ๊น์ด ๊ณตํต ์กฐ์์ผ๋ก lift the shared state up์ ํ๋ ๊ฒ์ ์ถ์ฒํ๋ค. ์ฆ 2๊ฐ์ ์์ ์ปดํฌ๋ํธ๊ฐ ๋ถ๋ชจ๋ก๋ถํฐ ๋์ผํ ๋ฐ์ดํฐ๋ฅผ ๊ณต์ ํ๋ ๊ฒฝ์ฐ, ๋ชจ๋ ์์ ์ปดํฌ๋ํธ์์ ๋ก์ปฌ ์ํ๋ฅผ ์ ์งํ๋ ๋์ ์ ์ํ๋ฅผ ๋ถ๋ชจ์์ ๊ด๋ฆฌํ๋ ๊ฒ์ด๋ค.
-
์ปดํฌ๋ํธ ๋ผ์ดํ ์ฌ์ดํด๋ ์ธ ๊ฐ์ง์ ๊ณ ์ ํ ๋จ๊ณ๊ฐ ์๋ค.
-
Mounting: ์ปดํฌ๋ํธ๊ฐ ๋ธ๋ผ์ฐ์ DOM์ mount ํ ์ค๋น๊ฐ ๋์๋ค. ์ด ๋จ๊ณ๋
constructor()
,getDerivedStateFromProps()
,render()
,componentDidMount()
๋ผ์ดํ ์ฌ์ดํด ๋ฉ์๋์ ์ด๊ธฐํ์ ๋ํด ๋ค๋ฃฌ๋ค. -
Updating: ์ด ๋จ๊ณ์์๋, ์ปดํฌ๋ํธ๋ ๋ ๊ฐ์ง ๋ฐฉ๋ฒ์ผ๋ก ์ ๋ฐ์ดํธ๊ฐ ๋๋๋ฐ, ์๋ก์ด props๋ฅผ ๋ณด๋ด๊ฑฐ๋,
setState()
๋๋forceUpdate()
์ผ๋ก state๋ฅผ ์ ๋ฐ์ดํธํ๋ ๊ฒ์ด๋ค. ์ด ๋จ๊ณ์์๋getDerivedStateFromProps()
,shouldComponentUpdate()
,render()
,getSnapshotBeforeUpdate()
,componentDidUpdate()
๋ผ์ดํ ์ฌ์ดํด ๋ฉ์๋๋ฅผ ๋ค๋ฃฌ๋ค. -
Unmounting: ์ด ๋ง์ง๋ง ๋จ๊ณ์์๋, ์ปดํฌ๋ํธ๋ ํ์ํ์ง ์์ผ๋ฉฐ ๋ธ๋ผ์ฐ์ DOM์์ unmount๋๋ค. ์ด ๋จ๊ณ์์๋
componentWillUnmount()
๋ผ์ดํ ์ฌ์ดํด ๋ฉ์๋๊ฐ ํฌํจ๋๋ค.
React๋ DOM์ ๋ณ๊ฒฝ ์ฌํญ์ ์ ์ฉํ ๋ ๋ด๋ถ์ ์ผ๋ก ๋จ๊ณ์ ๊ฐ๋ ์ ๊ฐ์ง๊ณ ์๋ค๋ ๊ฒ์ ์ธ๊ธํ ํ์๊ฐ ์๋ค. ๋ค์๊ณผ ๊ฐ์ด ๋ถ๋ฆฌ๋๋ค.
-
Render ์ปดํฌ๋ํธ๋ ๋ถ์ ํจ๊ณผ ์์ด ๋ ๋๋ง ๋๋ค. Pure ์ปดํฌ๋ํธ์ ์ ์ฉ๋๋ฉฐ ์ด ๋จ๊ณ์์ React๋ ๋ ๋๋ง์ ์ผ์ ์ ์ง, ์ค๋จ ๋๋ ์ฌ์์ํ ์ ์๋ค.
-
Pre-commit ์ปดํฌ๋ํธ๊ฐ ์ค์ ๋ก DOM์ ๋ณ๊ฒฝ์ฌํญ์ ์ ์ฉํ๊ธฐ ์ ์, React๊ฐ
getSnapshotBeforeUpdate()
๋ฅผ ํตํด์ DOM์ ์ฝ์ ์ ์๋ ์๊ฐ์ด๋ค. -
Commit React๋ DOM๊ณผ ํจ๊ป ์๋ํ๊ณ Mount๋ฅผ ์ํ
componentDidMount()
, ์ ๋ฐ์ดํธ๋ฅผ ์ํcomponentDidMount()
, Unmount๋ฅผ ์ํcomponentWillUnmount()
์ ์ต์ข ๋ผ์ดํ ์ฌ์ดํด์ ๊ฐ๊ฐ ์คํํ๋ค.
React 16.3+ ๋จ๊ณ (๋๋) ์ผ๋ฐ์ ์ธ ๋ฒ์ )
React 16.3 ์ ์๋
-
-
React 16.3+
- getDerivedStateFromProps:
render()
๊ฐ ํธ์ถ๋๊ธฐ ๋ฐ๋ก ์ ์ ํธ์ถ๋๋ฉฐ ๋ชจ๋ ๋ ๋๋ง์์ ํธ์ถ๋๋ค. ์ด์ state๊ฐ ํ์ํ ๋๋ฌธ ๊ฒฝ์ฐ์ ์ฌ์ฉ์ด ๋๋ค. ์ด์ state๊ฐ ํ์ํ๋ฉด ์ฝ์๋งํ ๊ฐ์น๊ฐ ์๋ค. - componentDidMount: ์ฒซ ๋ฒ์งธ ๋ ๋๋ง ์ดํ์ ์คํ๋๊ณ , ์ฌ๊ธฐ์ ๋ชจ๋ AJAX ์์ฒญ, DOM ๋๋ state ์ ๋ฐ์ดํธ ๋ฐ ์ด๋ฒคํธ ๋ฆฌ์ค๋๊ฐ ์ค์ ๋๋ค.
- shouldComponentUpdate: ์ปดํฌ๋ํธ๋ฅผ ์
๋ฐ์ดํธ ํด์ผ ํ ์ง ์ฌ๋ถ๋ฅผ ๊ฒฐ์ ํ๋ค. ๊ธฐ๋ณธ์ ์ผ๋ก
true
๋ฅผ ๋ฐํํ๋ค. state๋ props๊ฐ ์ ๋ฐ์ดํธ๋ ํ์ ์ปดํฌ๋ํธ๋ฅผ ๋ ๋๋งํ ํ์ ์๋ค๊ณ ํ์ ํ๋ ๊ฒฝ์ฐ false๋ฅผ ๋ฐํํ ์ ์๋ค. ์ปดํฌ๋ํธ๊ฐ ์๋ก์ด props๋ฅผ ๋ฐ์ผ๋ฉด ๋ฆฌ๋ ๋๋งํ๋ ๊ฒ์ ๋ฐฉ์งํ ์ ์์ด ์ฑ๋ฅ์ ํฅ์ํ๋๋ฐ ๋งค์ฐ ์ข๋ค. - getSnapshotBeforeUpdate: ๋ ๋๋ง ๋ ์ถ๋ ฅ์ด DOM์ ์ปค๋ฐ๋๊ธฐ ๋ฐ๋ก ์ง์ ์ ์คํ๋๋ค. ์ด๋ก ์ธํด ๋ชจ๋ ๊ฐ์
componentDidUpdate()
์ ์ ๋ฌ์ด ๋๋ค. DOM์์ ์คํฌ๋กค ์์น ๊ฐ์ ์ ๋ณด๋ฅผ ๊ฐ์ ธ์ค๋ ๋ฐ ์ ์ฉํ๋ค. - componentDidUpdate: ์ฃผ๋ก props๋ state์ ๋ณ๊ฒฝ์ ๋ํ ์๋ต์ผ๋ก DOM์ ์
๋ฐ์ดํธํ๋๋ฐ ์ฌ์ฉ๋๋ค.
shouldComponentUpdate()
๊ฐfalse
๋ฅผ ๋ฐํํ๋ฉด ์คํ๋์ง ์๋๋ค. - componentWillUnmount ๋๊ฐ๋ ๋คํธ์ํฌ ์์ฒญ์ ์ทจ์ํ๊ฑฐ๋ ์ปดํฌ๋ํธ์ ๊ด๋ จ๋ ๋ชจ๋ ์ด๋ฒคํธ ๋ฆฌ์ค๋๋ฅผ ์ ๊ฑฐํ๋ ๋ฐ ์ฌ์ฉ๋๋ค.
Before 16.3
- componentWillMount: ๋ ๋๋ง ์ ์ ์คํ๋๋ฉฐ root ์ปดํฌ๋ํธ์ App ์์ค ๊ตฌ์ฑ์ ํ๋ ๋ฐ ์ฌ์ฉ๋๋ค.
- componentDidMount: ์ฒซ ๋ฒ์งธ ๋ ๋๋ง ์ดํ์ ์คํ๋๊ณ , ์ฌ๊ธฐ์ ๋ชจ๋ AJAX ์์ฒญ, DOM ๋๋ state ์ ๋ฐ์ดํธ ๋ฐ ์ด๋ฒคํธ ๋ฆฌ์ค๋๊ฐ ์ค์ ๋๋ค.
- componentWillReceiveProps: ํน์ props ์ ๋ฐ์ดํธ๊ฐ state ์ ํ์ ์ผ์ผํฌ ๋ ์คํ๋๋ค.
- shouldComponentUpdate: ์ปดํฌ๋ํธ๋ฅผ ์
๋ฐ์ดํธ ํด์ผ ํ ์ง ์ฌ๋ถ๋ฅผ ๊ฒฐ์ ํ๋ค. ๊ธฐ๋ณธ์ ์ผ๋ก
true
๋ฅผ ๋ฐํํ๋ค. state๋ props๊ฐ ์ ๋ฐ์ดํธ๋ ํ์ ์ปดํฌ๋ํธ๋ฅผ ๋ ๋๋งํ ํ์ ์๋ค๊ณ ํ์ ํ๋ ๊ฒฝ์ฐ false๋ฅผ ๋ฐํํ ์ ์๋ค. ์ปดํฌ๋ํธ๊ฐ ์๋ก์ด props๋ฅผ ๋ฐ์ผ๋ฉด ๋ฆฌ๋ ๋๋งํ๋ ๊ฒ์ ๋ฐฉ์งํ ์ ์์ด ์ฑ๋ฅ์ ํฅ์ํ๋๋ฐ ๋งค์ฐ ์ข๋ค. - componentWillUpdate: true๋ฅผ ๋ฐํํ๋
shouldComponentUpdate()
์ ์ํด ํ์ธ๋ props ๋ฐ state ๋ณ๊ฒฝ์ด ์์ ๋ ์ปดํฌ๋ํธ๋ฅผ ๋ฆฌ๋ ๋๋งํ๊ธฐ ์ ์ ์คํ๋๋ค. - componentDidUpdate: ์ฃผ๋ก props๋ state ๋ณ๊ฒฝ์ ๋ํ ์๋ต์ผ๋ก DOM์ ์ ๋ฐ์ดํธํ๋ ๋ฐ ์ฌ์ฉ๋๋ค.
- componentWillUnmount: ๋๊ฐ๋ ๋คํธ์ํฌ ์์ฒญ์ ์ทจ์ํ๊ฑฐ๋ ์ปดํฌ๋ํธ์ ๊ด๋ จ๋ ๋ชจ๋ ์ด๋ฒคํธ ๋ฆฌ์ค๋๋ฅผ ์ ๊ฑฐํ๋ ๋ฐ ์ฌ์ฉ๋๋ค.
- getDerivedStateFromProps:
-
๊ณ ์ฐจ(Higher-Order) ์ปดํฌ๋ํธ (HOC)๋ ์ปดํฌ๋ํธ๋ฅผ ๊ฐ์ ธ์์ ์๋ก์ด ์ปดํฌ๋ํธ๋ฅผ ๋ฐํํ๋ ํจ์์ด๋ค. ๊ธฐ๋ณธ์ ์ผ๋ก, ์ปดํฌ๋ํธ ๊ตฌ์ฑ์์ ๋ณธ์ง์์ ํ์๋ ํจํด์ด๋ค.
๋์ ์ผ๋ก ์ ๊ณต๋ ์์ ์ปดํฌ๋ํธ๋ ํ์ฉํ ์ ์์ง๋ง, ์ ๋ ฅ ์ปดํฌ๋ํธ์ ์ด๋ ํ ๋์์ ์์ ํ๊ฑฐ๋ ๋ณต์ฌํ์ง ์์ผ๋ฏ๋ก ์์ ์ปดํฌ๋ํธ(pure components) ๋ผ๊ณ ๋ถ๋ฅธ๋ค.
const EnhancedComponent = higherOrderComponent(WrappedComponent)
HOC๋ ์๋์ ๊ฐ์ ๋ง์ ์ฌ๋ก์์ ์ฌ์ฉํ ์ ์๋ค.
- ์ฝ๋ ์ฌ์ฌ์ฉ, ๋ ผ๋ฆฌ ๋ฐ ๋ถํธ์คํธ๋ฉ ์ถ์ํ
- ๋์ฉ ๋ ๋๋ง
- State ์ถ์ํ ๋ฐ ์กฐ์
- Props ์กฐ์.
-
๋ค์๊ณผ ๊ฐ์ด props proxy ํจํด์ ์ฌ์ฉํ์ฌ ์ปดํฌ๋ํธ์ ์ ๋ฌ๋ props๋ฅผ ์ถ๊ฐ/ํธ์งํ ์ ์๋ค.
function HOC(WrappedComponent) { return class Test extends Component { render() { const newProps = { title: 'New Header', footer: false, showFeatureX: false, showFeatureY: true } return <WrappedComponent {...this.props} {...newProps} /> } } }
-
Context๋ ์๋์ผ๋ก ๋ชจ๋ ๋จ๊ณ์ props๋ฅผ ์ ๋ฌํ์ง ์๊ณ ์ปดํฌ๋ํธ ํธ๋ฆฌ๋ฅผ ํตํด ๋ฐ์ดํฐ๋ฅผ ์ ๋ฌํ๋ ๋ฐฉ๋ฒ์ ์ ๊ณตํ๋ค. ์๋ฅผ ๋ค์ด, ์ธ์ฆ๋ ์ฌ์ฉ์, locale ๊ธฐ๋ณธ ์ค์ , UI ํ ๋ง๋ ๋ง์ ์ปดํฌ๋ํธ๋ฅผ ํตํด ์์ฉ ํ๋ก๊ทธ๋จ์์ ์ก์ธ์คํด์ผ ํ๋ค.
const {Provider, Consumer} = React.createContext(defaultValue)
-
Children(
this.prop.children
)๋ ๋ค๋ฅธ prop์ ๋ง์ฐฌ๊ฐ์ง๋ก ๋ค๋ฅธ ์ปดํฌ๋ํธ์ ๋ฐ์ดํฐ๋ฅผ ์ ๋ฌํ ์ ์๋ ์์(this.prop.children
)๋ค. ์ปดํฌ๋ํธ์ ์ฌ๋ ํ๊ทธ์ ๋ซ๋ ํ๊ทธ ์ฌ์ด์ ๋์ธ ์ปดํฌ๋ํธ ํธ๋ฆฌ๊ฐ ํด๋น ์ปดํฌ๋ํธ์children
prop๋ก ์ ๋ฌ๋๋ค.์ด prop์ ์ฌ์ฉํ๊ธฐ ์ํด React API์์ ์ฌ์ฉํ ์ ์๋ ์ฌ๋ฌ ๊ฐ์ง ๋ฐฉ๋ฒ์ด ์๋ค. ์ฌ๊ธฐ์๋
React.Children.map
,React.Children.forEach
,React.Children.count
,React.Children.only
,React.Children.toArray
๊ฐ ํฌํจ๋๋ค. children prop์ ๊ฐ๋จํ ์ฌ์ฉ๋ฒ์ ๋ค์๊ณผ ๊ฐ๋ค.const MyDiv = React.createClass({ render: function() { return <div>{this.props.children}</div> } }) ReactDOM.render( <MyDiv> <span>{'Hello'}</span> <span>{'World'}</span> </MyDiv>, node )
-
React / JSX์ ์ฃผ์์ JavaScript Multiline ์ฃผ์๊ณผ ์ ์ฌํ์ง๋ง ์ค๊ดํธ๋ก ๋ฌถ์ธ๋ค.
Single-line comments:
<div> {/* ํ ์ค ์ฃผ์ (๋ฐ๋๋ผ ์๋ฐ ์คํฌ๋ฆฝํธ์์๋ ํ ์ค ์ฃผ์์ ์ด์ค ์ฌ๋์ (//)๋ก ํ์๋๋ค) */} {`Welcome ${user}, let's play React`} </div>
Multi-line comments:
<div> {/* ํ ์ค์ด์์ ์ฌ๋ฌ ์ค ์ฃผ์ */} {`Welcome ${user}, let's play React`} </div>
-
์์ ํด๋์ค ์์ฑ์๋
super()
๋ฉ์๋๊ฐ ํธ์ถ ๋ ๋๊น์งthis
๋ฅผ ์ฐธ์กฐํ ์ ์์ต๋๋ค. ES6 ํ์ ํด๋์ค์๋ ๋์ผํ๊ฒ ์ ์ฉ๋๋ค. super() ํธ์ถ์ props ๋งค๊ฐ ๋ณ์๋ฅผ ์ ๋ฌํ๋ ์ฃผ์ํ ์ด์ ๋ ์์ ์์ฑ์์์this.props
์ ์ ๊ทผํ๊ธฐ ์ํด์๋ค.props ์ ๋ฌ
class MyComponent extends React.Component { constructor(props) { super(props) console.log(this.props) // prints { name: 'John', age: 42 } } }
props ๋ฏธ์ ๋ฌ
class MyComponent extends React.Component { constructor(props) { super() console.log(this.props) // prints undefined // but props parameter is still available console.log(props) // prints { name: 'John', age: 42 } } render() { // no difference outside constructor console.log(this.props) // prints { name: 'John', age: 42 } } }
์ ์ฝ๋๋ฅผ ๋ณด๋ฉด
this.props
๊ฐ ์์ฑ์ ๋ด์์๋ง ๋ค๋ฅธ ๊ฒ์ ๋ณผ ์ ์๋ค. ์์ฑ์ ๋ฐ์์๋ ๋์ผํ๋ค. -
์ปดํฌ๋ํธ์ props๋ state๊ฐ ๋ณ๊ฒฝ๋์์ ๋, React๋ ์๋ก ๋ฐํ๋ ์๋ฆฌ๋จผํธ์ ์ด์ ์ ๋ ๋๋ง๋ ๊ฒ์ ๋น๊ตํด์ ์ค์ DOM์ด ์ ๋ฐ์ดํธ๊ฐ ํ์ํ์ง๋ฅผ ๊ฒฐ์ ํ๋ค. ๋๋ฑํ์ง ์์ ๋, React๊ฐ DOM์ ์ ๋ฐ์ดํธํ ๊ฒ์ด๋ค. ์ด ํ๋ก์ธ์ค๋ฅผ *์กฐ์ (reconciliation)*์ด๋ผ๊ณ ํ๋ค.
-
ES6๋ Babel transpiler๋ฅผ ์ฌ์ฉํ์ฌ JSX์ฝ๋๋ฅผ ๋ณํํ๋ ๊ฒฝ์ฐ ์ํ๋ ์์ฑ(property)๋ช ์ผ๋ก ์ค์ ํ ์ ์๋ค.
handleInputChange(event) { this.setState({ [event.target.id]: event.target.value }) }
-
ํจ์๋ฅผ ๋งค๊ฐ๋ณ์๋ก ์ ๋ฌํ๋ ๋์ ํจ์๊ฐ ํธ์ถ๋์ง ์๋๋ก ํ์ธํด์ผ ํ๋ค.
render() { // Wrong: handleClick is called instead of passed as a reference! return <button onClick={this.handleClick()}>{'Click Me'}</button> }
๋์ ์, ๊ดํธ ์์ด ํจ์ ์์ฒด๋ฅผ ์ ๋ฌํด์ผ ํ๋ค.
render() { // Correct: handleClick is passed as a reference! return <button onClick={this.handleClick}>{'Click Me'}</button> }
-
์ปดํฌ๋ํธ๋ DOM ์๋ฆฌ๋จผํธ๊ฐ ์๋๊ธฐ ๋๋ฌธ์ ๋๋ฌธ์๋ก ํ๊ธฐํด์ผ ํ๋ค. ๋ํ, JSX ์๋ฌธ์ ํ๊ทธ ์ด๋ฆ์ HTML ์๋ฆฌ๋จผํธ๋ฅผ ๋ํ๋ด๋ฉฐ ์ปดํฌ๋ํธ๋ ์ฐธ์กฐํ์ง ์๋๋ค.
-
class
๋ JavasScript์ ํค์๋์ด๋ฉฐ, JSX๋ JavaScript์ ํ์ฅ์ด๋ค. ์ด๊ฒ์ด React๊ฐclass
๋์className
์ ์ฌ์ฉํ๋ ์ฃผ๋ ์ด์ ์ด๋ค.className
prop์ผ๋ก ๋ฌธ์์ด์ ๋๊ฒจ์ผ ํ๋ค.render() { return <span className={'menu navigation-menu'}>{'Menu'}</span> }
-
์ปดํฌ๋ํธ๊ฐ ์ฌ๋ฌ ์๋ฆฌ๋จผํธ๋ฅผ ๋ฐํํ๋ ๋ฐ ์ฌ์ฉํ๋ React์ ์ผ๋ฐ์ ์ธ ํจํด์ด๋ค. Fragments๋ฅผ ์ฌ์ฉํ๋ฉด DOM์ ๋ ธ๋๋ฅผ ์ถ๊ฐํ์ง ์๊ณ ์์ ๋ชฉ๋ก์ ๊ทธ๋ฃนํํ ์ ์๋ค.
render() { return ( <React.Fragment> <ChildA /> <ChildB /> <ChildC /> </React.Fragment> ) }
๋์ฑ๋ ์งง์ ๊ตฌ๋ฌธ๋ ์์ง๋ง, ๋ง์ ๋๊ตฌ์์ ์ง์ํ์ง ์๋๋ค.
render() { return ( <> <ChildA /> <ChildB /> <ChildC /> </> ) }
-
- Fragments๋ ์กฐ๊ธ ๋ ๋น ๋ฅด๋ฉฐ ์ฌ๋ถ์ DOM ๋ ธ๋๋ฅผ ๋ง๋ค์ง ์์์ ๋ฉ๋ชจ๋ฆฌ๋ฅผ ๋ ์ฌ์ฉํ๋ค. ์ด๊ฒ์ ๋งค์ฐ ํฌ๊ณ ๊น์ ํธ๋ฆฌ(tree)์์ ์ค์ง์ ์ธ ์ด๋์ ๊ฐ์ ธ๋ค์ค๋ค.
- Flexbox์ CSS Grid ๊ฐ์ ์ผ๋ถ CSS ๋ฉ์ปค๋์ฆ์๋ ํน์ํ ๋ถ๋ชจ-์์ ๊ด๊ณ๋ฅผ ๊ฐ์ง๊ณ ์์ด์, ์ค๊ฐ์ div๋ฅผ ์ถ๊ฐํ๋ฉด ์ํ๋ ๋ ์ด์์์ ์ ์งํ๊ธฐ๊ฐ ์ด๋ ต๋ค.
- DOM Inspector๊ฐ ๋ ๋ณต์กํด์ง๋ค.
-
Portal์ ์์ ์ปดํฌ๋ํธ์ DOM ๊ณ์ธต ๊ตฌ์กฐ ์ธ๋ถ์ ์๋ DOM ๋ ธ๋์ ์์์ ๋ ๋๋งํ๋๋ฐ ๊ถ์ฅ๋๋ ๋ฐฉ๋ฒ์ด๋ค.
ReactDOM.createPortal(child, container)
์ฒซ ๋ฒ์งธ ์ธ์๋ ๋ ๋๋ง ๊ฐ๋ฅํ React ์๋ฆฌ๋จผํธ, ๋ฌธ์์ด, fragment ๊ฐ์ ํ์์์์ด๋ค. ๋ ๋ฒ์งธ ์ธ์๋ DOM ์๋ฆฌ๋จผํธ์ด๋ค.
-
๋์์ด state์ ๊ด๋ จ์ด ์๋ ๊ฒฝ์ฐ stateless ์ปดํฌ๋ํธ๊ฐ ๋ ์ ์๋ค. function์ด๋ class๋ฅผ ์ฌ์ฉํด์ stateless ์ปดํฌ๋ํธ๋ฅผ ๋ง๋ค ์ ์๋ค. ์ปดํฌ๋ํธ์์ ๋ผ์ดํ ์ฌ์ดํด ํ ์ ์ฌ์ฉํด์ผ ํ๋ ๊ฒฝ์ฐ๊ฐ ์๋๋ผ๋ฉด function ์ปดํฌ๋ํธ๋ฅผ ์ฌ์ฉํ๋๊ฒ ์ข๋ค. function ์ปดํฌ๋ํธ๋ฅผ ์ฌ์ฉํ๋ฉด ์ฌ๋ฌ ์ด์ ์ด ์๋๋ฐ, ์ฐ๊ธฐ, ์ดํด ๋ฐ ํ ์คํธํ๊ธฐ๊ฐ ์ฝ๊ณ ๋ ๋น ๋ฅด๋ฉฐ,
this
ํค์๋๋ฅผ ํผํ ์ ์๋ค. -
์ปดํฌ๋ํธ์ ๋์์ด ์ปดํฌ๋ํธ์ state์ ๋ฐ๋ผ ๋ฌ๋ผ์ง๋ค๋ฉด stateful ์ปดํฌ๋ํธ๋ผ๊ณ ํ ์ ์๋ค. stateful ์ปดํฌ๋ํธ๋ ํญ์ class ์ปดํฌ๋ํธ ์ด๋ฉฐ
constructor
์์ ์ด๊ธฐํ๊ฐ ๋์ด state๋ฅผ ๊ฐ์ง๊ฒ ๋๋ค.class App extends Component { constructor(props) { super(props) this.state = { count: 0 } } render() { // ... } }
-
์์ฉ ํ๋ก๊ทธ๋จ์ด development ๋ชจ๋์์ ์คํ๋ ๋, React๋ ์๋์ผ๋ก ์ปดํฌ๋ํธ์ ์ค์ ํ ๋ชจ๋ props๋ฅผ ํ์ธํ์ฌ ์ฌ๋ฐ๋ฅธ ํ์ ์ธ์ง ํ์ธํ๋ค. ํ์ ์ด ๋ง์ง ์๋๋ค๋ฉด, React๋ ์ฝ์์ ๊ฒฝ๊ณ ๋ฉ์์ง๋ฅผ ๋์ด๋ค. production ๋ชจ๋์์๋ ์ฑ๋ฅ์ ์ํฅ์ ๋ฏธ์น๋ฏ๋ก ์ฌ์ฉํ ์ ์๋ค. ์ฃผ์ํ props๋
isRequired
๋ก ์ ์๋๋ค.์ฌ์ ์ ์๋ props ํ์ ์ด๋ค.
PropTypes.number
PropTypes.string
PropTypes.array
PropTypes.object
PropTypes.func
PropTypes.node
PropTypes.element
PropTypes.bool
PropTypes.symbol
PropTypes.any
๋ค์๊ณผ ๊ฐ์ด
User
์ปดํฌ๋ํธ์ ๋ํpropTypes
์ ์ ์ํ ์ ์๋ค.import React from 'react' import PropTypes from 'prop-types' class User extends React.Component { static propTypes = { name: PropTypes.string.isRequired, age: PropTypes.number.isRequired } render() { return ( <> <h1>{`Welcome, ${this.props.name}`}</h1> <h2>{`Age, ${this.props.age}`}</h2> </> ) } }
Note: React v15.5๋ฒ์ ์์ PropTypes๋
React.PropTypes
์์prop-types
๋ผ์ด๋ธ๋ฌ๋ฆฌ๋ก ์ด๋๋์๋ค. -
- Virtual DOM์ผ๋ก ์ ํ๋ฆฌ์ผ์ด์ ์ ์ฑ๋ฅ์ด ํฅ์๋๋ค.
- JSX๋ ์ฝ๋๋ฅผ ์ฝ๊ณ ์ฐ๊ธฐ ์ฝ๊ฒ ํด์ค๋ค.
- ํด๋ผ์ด์ธํธ์ ์๋ฒ์ฌ์ด๋(SSR) ๋ ๋๋ง์ด ๋ ๋ค ๊ฐ๋ฅํ๋ค.
- ์ค์ง view ๋ผ์ด๋ธ๋ฌ๋ฆฌ์ด๊ธฐ ๋๋ฌธ์, ํ๋ ์์ํฌ(Angular, Backbone)์ ์ฝ๊ฒ ํตํฉ์ด ๊ฐ๋ฅํ๋ค.
- Jest์ ๊ฐ์ ํด์ ์ฌ์ฉํ์ฌ ๋จ์ ๋ฐ ํตํฉ ํ ์คํธ๋ฅผ ์ฝ๊ฒ ์์ฑํ ์ ์๋ค.
-
- React๋ ํ๋ ์์ํฌ๊ฐ ์๋, view ๋ผ์ด๋ธ๋ฌ๋ฆฌ์ด๋ค.
- ์น ๊ฐ๋ฐ์ ์ฒ์ ์ ํ๋ ์ด๋ณด์์๊ฒ ๋ฌ๋ ์ปค๋ธ๊ฐ ์กด์ฌํ๋ค.
- ๊ธฐ์กด MVC ํ๋ ์์ํฌ์ React๋ฅผ ํตํฉํ๋ ค๋ฉด ๋ช ๊ฐ์ง ์ถ๊ฐ ๊ตฌ์ฑ์ด ํ์ํ๋ค.
- ์ธ๋ผ์ธ ํ ํ๋ฆฟ๊ณผ JSX๋ก ์ธํด ์ฝ๋ ๋ณต์ก์ฑ์ด ์ฆ๊ฐํ๋ค.
- ๋๋ฌด ๋ง์ ์์ ์ปดํฌ๋ํธ๋ ์์ง๋์ด๋ง ๋๋ ๋ณด์ผ๋ฌ ํ๋ ์ดํธ๋ก ์ด์ด์ง๋ค.
-
Error boundaries๋ ํ์ ์ปดํฌ๋ํธ์ ๋ชจ๋ ์์น์์ JavaScript ์ค๋ฅ๋ฅผ catchํ๊ณ , ์ค๋ฅ๋ฅผ ๊ธฐ๋กํ๋ฉฐ, ์ค๋ฅ๊ฐ ๋ฐ์ํ ์ปดํฌ๋ํธ ํธ๋ฆฌ ๋์ ํด๋ฐฑ(fallback) UI๋ฅผ ํ์ํ๋ ์ปดํฌ๋ํธ์ด๋ค.
ํด๋์ค ์ปดํฌ๋ํธ๋
componentDidCatch(error, info)
๋๋static getDerivedStateFromError()
๋ผ๋ ์๋ก์ด ๋ผ์ดํ ์ฌ์ดํด ๋ฉ์๋๋ฅผ ์ ์ํ๋ฉด error boundary๊ฐ ๋๋ค.class ErrorBoundary extends React.Component { constructor(props) { super(props) this.state = { hasError: false } } componentDidCatch(error, info) { // You can also log the error to an error reporting service logErrorToMyService(error, info) } static getDerivedStateFromError(error) { // Update state so the next render will show the fallback UI. return { hasError: true }; } render() { if (this.state.hasError) { // You can render any custom fallback UI return <h1>{'Something went wrong.'}</h1> } return this.props.children } }
๊ทธ๋ฐ ๋ค์ ์ผ๋ฐ ์ปดํฌ๋ํธ๋ฅผ ์ฌ์ฉํ๋ฉด ๋๋ค.
<ErrorBoundary> <MyWidget /> </ErrorBoundary>
-
React v15์์๋
unstable_handleError
๋ฉ์๋๋ฅผ ์ฌ์ฉํ์ฌ error boundaries ์ ๋ํ ๋งค์ฐ ๊ธฐ๋ณธ์ ์ธ ์ง์์ ์ ๊ณตํ๋ค. React v16์์componentDidCatch
๋ก ์ด๋ฆ์ด ๋ณ๊ฒฝ๋์๋ค. -
์ผ๋ฐ์ ์ผ๋ก ์ฐ๋ฆฌ๋ React ์ ํ๋ฆฌ์ผ์ด์ ์์ ํ์ ๊ฒ์ฌ๋ฅผ ์ํด PropTypes ๋ผ์ด๋ธ๋ฌ๋ฆฌ๋ฅผ ์ฌ์ฉํ๋ค. (React v15.5 ์ดํ
React.PropTypes
๋prop-types
ํจํค์ง๋ก ์ฎ๊ฒจ์ง.) ํฐ ์ฝ๋ ๊ธฐ๋ฐ์ ๊ฒฝ์ฐ, ์ปดํ์ผ ํ์์ ํ์ ๊ฒ์ฌ๋ฅผ ํ๊ณ ์๋ ์์ฑ ๊ธฐ๋ฅ์ ์ ๊ณตํ๋ Flow๋ TypeScript ๊ฐ์ ์ ์ ํ์ ๊ฒ์ฌ๊ธฐ๋ฅผ ์ฌ์ฉํ๋ ๊ฒ์ด ์ข๋ค. -
react-dom
ํจํค์ง๋ ์ฑ์ ์ต์์ ๋ ๋ฒจ์์ ์ฌ์ฉํ ์ ์๋ DOM-๊ด๋ จ ๋ฉ์๋๋ฅผ ์ ๊ณตํ๋ค. ๋๋ถ๋ถ์ ์ปดํฌ๋ํธ๋ ์ด ๋ชจ๋์ ์ฌ์ฉํ ํ์๊ฐ ์๋ค. ์ด ํจํค์ง์ ์ผ๋ถ ๋ฉ์๋๋ ์๋์ ๊ฐ๋ค.render()
hydrate()
unmountComponentAtNode()
findDOMNode()
createPortal()
-
์ด ๋ฉ์๋๋ React ์๋ฆฌ๋จผํธ๋ฅผ ์ ๊ณต๋ ์ปจํ ์ด๋์ DOM์ ๋ ๋๋งํ๊ณ ์ปดํฌ๋ํธ์ ๋ํ ์ฐธ์กฐ๋ฅผ ๋ฐํํ๋ ๋ฐ ์ฌ์ฉ๋๋ค. React ์๋ฆฌ๋จผํธ๊ฐ ์ด์ ์ ์ปจํ ์ด๋๋ก ๋ ๋๋ง ๋ ๊ฒฝ์ฐ, ํด๋น ์๋ฆฌ๋จผํธ์ ๋ํ ์ ๋ฐ์ดํธ๋ฅผ ์ํํ๊ณ ํ์์ ๋ฐ๋ผ ์ต์ ๋ณ๊ฒฝ ์ฌํญ์ ๋ฐ์ํ๊ธฐ ์ํด DOM์ ๋ณ๊ฒฝํ๋ค.
ReactDOM.render(element, container[, callback])
์ ํ์ ์ฝ๋ฐฑ์ด ์ ๊ณต๋๋ฉด, ์ปดํฌ๋ํธ๊ฐ ๋ ๋๋ง ๋๊ฑฐ๋ ์ ๋ฐ์ดํธ ๋์ ๋ ์คํ๋๋ค.
-
ReactDOMServer
๊ฐ์ฒด๋ฅผ ์ฌ์ฉํ๋ฉด ์ปดํฌ๋ํธ๋ฅผ ์ ์ ๋งํฌ์ (์ผ๋ฐ์ ์ผ๋ก ๋ ธ๋ ์๋ฒ์์ ์ฌ์ฉํ๋ค.)์ผ๋ก ๋ ๋๋งํ ์ ์๋ค. ์ด ๊ฐ์ฒด๋ ์ฃผ๋ก ์๋ฒ ์ฌ์ด๋ ๋ ๋๋ง(SSR) ์ ์ฌ์ฉ๋๋ค. ์๋์ ๋ฉ์๋๋ค์ ์๋ฒ์ ๋ธ๋ผ์ฐ์ ํ๊ฒฝ์์ ๋ชจ๋ ์ฌ์ฉํ ์ ์๋ค.renderToString()
renderToStaticMarkup()
์๋ฅผ ๋ค์ด, ์ผ๋ฐ์ ์ผ๋ก Express, Hapi, ๋๋ Koa์ ๊ฐ์ ๋ ธ๋ ๊ธฐ๋ฐ ์น ์๋ฒ๋ฅผ ์คํํ๊ณ
renderToString
๋ฅผ ํธ์ถํ์ฌ ๋ฃจํธ ์ปดํฌ๋ํธ๋ฅผ ๋ฌธ์์ด๋ก ๋ ๋๋งํ ๋ค์ ์๋ต์ผ๋ก ๋ณด๋ธ๋ค.// using Express import { renderToString } from 'react-dom/server' import MyPage from './MyPage' app.get('/', (req, res) => { res.write('<!DOCTYPE html><html><head><title>My Page</title></head><body>') res.write('<div id="content">') res.write(renderToString(<MyPage/>)) res.write('</div></body></html>') res.end() })
-
dangerouslySetInnerHTML
๋ ๋ธ๋ผ์ฐ์ DOM์์innerHTML
๋ฅผ ์ฌ์ฉํ๊ธฐ ์ํ React์ ๋์ฒด ์์ฑ์ด๋ค.innerHTML
๊ณผ ๊ฐ์ด, ํฌ๋ก์ค-์ฌ์ดํธ ์คํฌ๋ฆฝํ (XSS) ๊ณต๊ฒฉ์ ๊ณ ๋ คํ์ฌ ์ด ์์ฑ์ ์ฌ์ฉํ๋ ๊ฒ์ ์ํํ๋ค.__html
๊ฐ์ฒด๋ฅผ ํค๋ก ์ ๋ฌํ๊ณ HTML ํ ์คํธ๋ฅผ ๊ฐ์ผ๋ก ์ ๋ฌํ๋ฉด ๋๋ค.์ด ์์ ์์ MyComponent๋ HTML ๋งํฌ์ ์ค์ ์ ์ํด
dangerouslySetInnerHTML
์์ฑ์ ์ฌ์ฉํ๋ค.function createMarkup() { return { __html: 'First · Second' } } function MyComponent() { return <div dangerouslySetInnerHTML={createMarkup()} /> }
-
style
์์ฑ์ CSS ๋ฌธ์์ด ๋์ ์ camelCased ์์ฑ์ด ์๋ JavaScript ๊ฐ์ฒด๋ฅผ ์ฌ์ฉํ๋ค. ์ด๊ฒ์ DOM ์คํ์ผ JavaScript ์์ฑ๊ณผ ์ผ์นํ๋ฉฐ, ๋ณด๋ค ํจ์จ์ ์ด๊ณ , XSS ๋ณด์ ์ทจ์ฝ์ ์ ๋ฐฉ์งํ๋ค.const divStyle = { color: 'blue', backgroundImage: 'url(' + imgUrl + ')' }; function HelloWorldComponent() { return <div style={divStyle}>Hello World!</div> }
์คํ์ผ ํค๋ JavaScript์์ DOM ๋ ธ๋์ ์์ฑ(e.g.
node.style.backgroundImage
)์ ์ก์ธ์คํ๋ ๊ฒ๊ณผ ์ผ๊ด์ฑ์ ์ ์งํ๊ธฐ ์ํด์ camelCased๋ก ์์ฑํ๋ค. -
React ์๋ฆฌ๋จผํธ์ ์ด๋ฒคํธ ์ฒ๋ฆฌ์๋ ๋ค์๊ณผ ๊ฐ์ ๋ฌธ๋ฒ์ ์ฐจ์ด๊ฐ ์๋ค.
- React ์ด๋ฒคํธ ํธ๋ค๋ฌ๋ ์๋ฌธ์๊ฐ ์๋ camelCase๋ฅผ ์ฌ์ฉํ์ฌ ๋ช ๋ช ๋๋ค.
- JSX์์๋ ๋ฌธ์์ด์ด ์๋ ์ด๋ฒคํธ ํธ๋ค๋ฌ๋ก ํจ์๋ฅผ ์ ๋ฌํ๋ค.
-
setState()
๋ฅผ ์ฌ์ฉํ๋ฉด, React์ ๊ฐ์ฒด state๋ฅผ ํ ๋นํ๋ ๊ฒ๊ณผ ๋ณ๊ฐ๋ก ์ปดํฌ๋ํธ์ ๋ชจ๋ ์์์ ๋ฆฌ๋ ๋๋งํ๋ค. ๋ค์๊ณผ ๊ฐ์ ์ค๋ฅ๊ฐ ๋ฐ์ํ๋ค. Can only update a mounted or mounting component. ๊ทธ๋์this.state
๋ฅผ ์ฌ์ฉํ์ฌ ์์ฑ์ ๋ด๋ถ์ ๋ณ์๋ฅผ ์ด๊ธฐํํด์ผ ํ๋ค. -
React๊ฐ ์๋ฆฌ๋จผํธ๋ฅผ ์ถ์ ํ ์ ์๋๋ก ํค๋ ์์ ์ ์ด๊ณ ์์ธก ๊ฐ๋ฅํ๋ฉฐ ๊ณ ์ ํด์ผ ํ๋ค.
์๋์ ์ฝ๋ ์กฐ๊ฐ์์ ๊ฐ ์๋ฆฌ๋จผํธ์ ํค๋ ํํ๋๋ ๋ฐ์ดํฐ์ ๋ฌถ์ด์ง ์๊ณ ์์๋ฅผ ๊ธฐ๋ฐ์ผ๋ก ํ๋ค. ์ด๊ฒ์ React๊ฐ ํ ์ ์๋ ์ต์ ํ๋ฅผ ์ ํํ๋ค.
{todos.map((todo, index) => <Todo {...todo} key={index} /> )}
์ ๋ํฌํ ํค์ ์๋ฆฌ๋จผํธ ๋ฐ์ดํฐ๋ฅผ ์ฌ์ฉํ๋ ๊ฒฝ์ฐ, todo.id๊ฐ ์ด ๋ชฉ๋ก์์ ์ ์ผํ๊ณ ์์ ์ ์ด๋ผ๊ณ ๊ฐ์ ํ๋ฉด, React๋ ์์๋ฅผ ์ฌํ๊ฐ ํ ํ์์์ด ์๋ฆฌ๋จผํธ๋ฅผ ์ฌ์ ๋ ฌ ํ ์ ์๋ค.
{todos.map((todo) => <Todo {...todo} key={todo.id} /> )}
-
componentWillMount()
๋ผ์ดํ ์ฌ์ดํด ๋ฉ์๋์์ ๋น๋๊ธฐ ์ด๊ธฐํ๋ฅผ ํ์ง ์๋๊ฒ ์ข๋ค.componentWillMount()
๋ ๋ง์ดํธ๊ฐ ๋ฐ์ํ๊ธฐ ์ง์ ์ ํธ์ถ๋๋ค.render()
์ ์ ํธ์ถ๋๊ธฐ ๋๋ฌธ์ ๋ฉ์๋์์ state๋ฅผ ์ค์ ํ๋ฉด ๋ฆฌ๋ ๋๋ง ๋์ง ์๋๋ค. ๋ค์๊ณผ ๊ฐ์ ๋ฉ์๋๋ก ์ฌ์ด๋ ์ดํํธ๋ ๊ตฌ๋ ์ ํผํ๋ฉด๋๋ค.componentWillMount()
๋์ ์componentDidMount()
์์ ์ปดํฌ๋ํธ ์ด๊ธฐํ์ ๋ํ ๋น๋๊ธฐ ํธ์ถ์ ํ๋ค.componentDidMount() { axios.get(`api/todos`) .then((result) => { this.setState({ messages: [...result.data] }) }) }
-
์ปดํฌ๋ํธ๋ฅผ ์๋ก ๊ณ ์นจ ์์ด ์ปดํฌ๋ํธ์ props๋ฅผ ๋ณ๊ฒฝํ๋ฉด, ์์ฑ์ ํจ์๊ฐ ์ ๋๋ก ์ปดํฌ๋ํธ์ ํ์ฌ state๋ฅผ ์ ๋ฐ์ดํธํ์ง ์์ผ๋ฏ๋ก ์๋ก์ด props ๊ฐ์ด ํ์๋์ง ์๋๋ค. props๋ก state ์ด๊ธฐํํ๋ ๊ฒ์ ์ปดํฌ๋ํธ๊ฐ ์ฒ์ ๋ง๋ค์ด์ง ๋๋ง ์คํ๋๋ค.
์๋์ ์ปดํฌ๋ํธ๋ ์ ๋ฐ์ดํธ๋ ์ ๋ ฅ๊ฐ์ ํ์ํ์ง ์๋๋ค.
class MyComponent extends React.Component { constructor(props) { super(props) this.state = { records: [], inputValue: this.props.inputValue }; } render() { return <div>{this.state.inputValue}</div> } }
render ๋ฉ์๋ ์์์ props๋ฅผ ์ฌ์ฉํ๋ฉด ๊ฐ์ด ์ ๋ฐ์ดํธ๋๋ค.
class MyComponent extends React.Component { constructor(props) { super(props) this.state = { record: [] } } render() { return <div>{this.props.inputValue}</div> } }
-
๊ฒฝ์ฐ์ ๋ฐ๋ผ ์ผ๋ถ state์ ๋ฐ๋ผ ๋ค๋ฅธ ์ปดํฌ๋ํธ๋ฅผ ๋ ๋๋งํ๋ ค๊ณ ํ๋ค. JSX๋
false
๋๋undefined
๋ฅผ ๋ ๋๋งํ์ง ์์ ์กฐ๊ฑด๋ถ ๋จ๋ฝ์ ์ฌ์ฉํ์ฌ ํน์ ์กฐ๊ฑด์ด true์ธ ๊ฒฝ์ฐ์๋ง ์ปดํฌ๋ํธ์ ์ฃผ์ด์ง ๋ถ๋ถ์ ๋ ๋๋งํ ์ ์๋ค.const MyComponent = ({ name, address }) => ( <div> <h2>{name}</h2> {address && <p>{address}</p> } </div> )
if-else
์กฐ๊ฑด์ด ํ์ํ๋ฉด ์ผํญ ์ฐ์ฐ์๋ฅผ ์ฌ์ฉํ๋ฉด ๋๋ค.const MyComponent = ({ name, address }) => ( <div> <h2>{name}</h2> {address ? <p>{address}</p> : <p>{'Address is not available'}</p> } </div> )
-
spread props ๋ฅผ ์ฌ์ฉํ ๋ ์ ์ ์๋ HTML ์์ฑ์ ์ถ๊ฐํ ์ํ์ด ์๋ค. ์ด๋ ๋์ ์ต๊ด์ด๋ค. ๋์ ์ฐ๋ฆฌ๋
...rest
์ฐ์ฐ์๋ก props destructuring์ ์ฌ์ฉํ ์ ์์ผ๋ฏ๋ก, ํ์ํ props๋ง ์ถ๊ฐํ ์ ์๋ค. ์๋ฅผ ๋ค์ด,const ComponentA = () => <ComponentB isDisplay={true} className={'componentStyle'} /> const ComponentB = ({ isDisplay, ...domProps }) => <div {...domProps}>{'ComponentB'}</div>
-
์ปดํฌ๋ํธ๋ฅผ ํจ์๋ก ์ ๋ฌํ๋ ๊ฒ๊ณผ ๋์ผํ๊ฒ, class ์ปดํฌ๋ํธ๋ฅผ ๊พธ๋ฐ ์ ์๋ค. ๋ฐ์ฝ๋ ์ดํฐ๋ ์ปดํฌ๋ํธ ๊ธฐ๋ฅ์ ์์ ํ๋ ์ ์ฐํ๊ณ ์ฝ๊ธฐ ์ฌ์ด ๋ฐฉ๋ฒ์ด๋ค.
@setTitle('Profile') class Profile extends React.Component { //.... } /* title์ ๋ฌธ์ ์ ๋ชฉ์ผ๋ก ์ค์ ๋ ๋ฌธ์์ด์ด๋ค. WrappedComponent๋ ์ฐ๋ฆฌ์ ๋ฐ์ฝ๋ ์ดํฐ๊ฐ ์์ ์์ ์์ ๋ณผ ์ ์๋ฏ์ด ์ปดํฌ๋ํธ ํด๋์ค ๋ฐ๋ก ์์ ๋๋๋ค. */ const setTitle = (title) => (WrappedComponent) => { return class extends React.Component { componentDidMount() { document.title = title } render() { return <WrappedComponent {...this.props} /> } } }
Note: ๋ฐ์ฝ๋ ์ดํฐ๋ ES7์ ํฌํจ๋์ง ์์์ง๋ง, ํ์ฌ stage 2 proposal์ด๋ค.
-
ํจ์ํ ์ปดํฌ๋ํธ์ ์ฌ์ฉํ ์ ์๋ ๋ฉ๋ชจ ๋ผ์ด๋ธ๋ฌ๋ฆฌ๊ฐ ์๋ค. ์๋ฅผ ๋ค์ด
moize
๋ผ์ด๋ธ๋ฌ๋ฆฌ๋ ๋ค๋ฅธ ์ปดํฌ๋ํธ์ ์ปดํฌ๋ํธ๋ฅผ ๋ฉ๋ชจํ ์ ์๋ค.import moize from 'moize' import Component from './components/Component' // this module exports a non-memoized component const MemoizedFoo = moize.react(Component) const Consumer = () => { <div> {'I will memoize the following entry:'} <MemoizedFoo/> </div> }
-
React๋ ์ด๋ฏธ ๋ ธ๋ ์๋ฒ์์ ๋ ๋๋ง์ ์ฒ๋ฆฌํ ์ ์๋๋ก ์ค๋น๋์ด ์๋ค. DOM ๋ ๋๋ฌ์ ํน์ ๋ฒ์ ์ ์ฌ์ฉํ ์ ์์ผ๋ฉฐ ํด๋ผ์ด์ธํธ ์ธก๊ณผ ๋์ผํ ํจํด์ ๋ฐ๋ฅธ๋ค.
import ReactDOMServer from 'react-dom/server' import App from './App' ReactDOMServer.renderToString(<App />)
์ด ๋ฉ์๋๋ ์ผ๋ฐ HTML์ ๋ฌธ์์ด๋ก ์ถ๋ ฅํ๋ฉฐ, ์๋ฒ ์๋ต์ ์ผ๋ถ๋ก ํ์ด์ง ๋ณธ๋ฌธ ๋ด์ ๋ฐฐ์นํ ์ ์๋ค. ํด๋ผ์ด์ธํธ ์ธก์์ React๋ ์ฌ์ ๋ ๋๋ง๋ ์ปจํ ์ธ ๋ฅผ ํ์งํ๊ณ ์ค๋จ๋ ๋ถ๋ถ์ ์๋ฒฝํ๊ฒ ํ์ ํ๋ค.
-
Webpack์
DefinePlugin
๋ฉ์๋๋ฅผ ์ฌ์ฉํด์NODE_ENV
๋ฅผproduction
ํ๊ฒฝ์ผ๋ก ์ค์ ํด์ผ propType ์ ํจ์ฑ ๊ฒ์ฌ ๋ฐ ์ถ๊ฐ ๊ฒฝ๊ณ ์ ๊ฐ์ ์ฌํญ์ ์ ๊ฑฐํ ์ ์๋ค. ์ด์๋ ๋ณ๊ฐ๋ก, Uglify์ ๊ฐ๋ฐ ์ฝ๋์ ์ฃผ์์ ์ ๊ฑฐํ๋ ๋ฐ๋ ์ฝ๋(dead-code) ์ ๊ฑฐ ๊ธฐ๋ฅ์ ์ฌ์ฉํ์ฌ ๋ฒ๋ค ํฌ๊ธฐ๋ฅผ ํฌ๊ฒ ์ค์ผ ์ ์๋ค. -
create-react-app
CLI ํด์ ๋ณ๋์ ๊ตฌ์ฑ๋จ๊ณ ์์ด ๋น ๋ฅด๊ฒ React ์ ํ๋ฆฌ์ผ์ด์ ์ ๋ง๋ค๊ณ ์คํํ ์ ์๋ค.CRA๋ก Todo ์ฑ์ ๋ง๋ค์ด๋ณด์
# Installation $ npm install -g create-react-app # Create new project $ create-react-app todo-app $ cd todo-app # Build, test and run $ npm run build $ npm run test $ npm start
React ์ฑ์ ์ ์ํ๋ ๋ฐ ํ์ํ ๋ชจ๋ ๊ฒ์ด ํฌํจ๋์ด ์๋ค.
- React, JSX, ES6, ๋ฐ Flow ๋ฌธ๋ฒ ์ง์.
- object spread operator์ ๊ฐ์ ES6๋ฅผ ๋์ด์ ์ธ์ด ํ์ฅ ๊ธฐ๋ฅ.
- ์๋ ์ ๋์ด๊ฐ ๋ถ์ CSS๋ก, -webkit- ์ด๋ ๋ค๋ฅธ ์ ๋์ฌ๋ ํ์ ์๋ค.
- coverage๋ฅผ ์ง์ํ๋ ๋ด์ฅ๋ ๋น ๋ฅธ ๋ํํ ๋จ์ ํ ์คํธ ๋ฌ๋.
- ์ผ๋ฐ์ ์ธ ์ค์์ ๋ํด์ ๊ฒฝ๊ณ ํ๋ ๋ผ์ด๋ธ ๊ฐ๋ฐ ์๋ฒ.
- hashes์ sourcemaps์ ์ฌ์ฉํ์ฌ production ์ฉ JS, CSS ๋ฐ images๋ฅผ ๋ฌถ๋ ๋น๋ ์คํฌ๋ฆฝํธ
-
๋ผ์ดํ ์ฌ์ดํด ๋ฉ์๋๋ ์ปดํฌ๋ํธ ์ธ์คํด์ค๊ฐ ์์ฑ๋์ด DOM์ ์ฝ์ ๋ ๋ ๋ค์๊ณผ ๊ฐ์ ์์๋ก ํธ์ถ๋๋ค.
constructor()
static getDerivedStateFromProps()
render()
componentDidMount()
-
๋ค์ ๋ผ์ดํ ์ฌ์ดํด ๋ฉ์๋๋ ์์ ํ์ง ์์ ์ฝ๋ฉ ๋ฐฉ๋ฒ์ด๋ฉฐ ๋น๋๊ธฐ ๋ ๋๋ง์์๋ ๋์ฑ ๋ฌธ์ ๋ ๊ฒ์ด๋ค.
componentWillMount()
componentWillReceiveProps()
componentWillUpdate()
React v16.3์์ ์์ ๋ฉ์๋๋ค์
UNSAFE_
์ ๋์ด๊ฐ ๋ณ์นญ์ผ๋ก ๋ถ์ด์ฃผ๊ธฐ ์์ํ์ผ๋ฉฐ, React v17์์ ์ ๋์ด๊ฐ ์๋ ๋ฒ์ ์ ์ ๊ฑฐ๋๋ค. -
์๋ก์ด ์ ์
getDerivedStateFromProps()
๋ผ์ดํ ์ฌ์ดํด ๋ฉ์๋๋ ์ปดํฌ๋ํธ๊ฐ ์ธ์คํด์คํ๋ ํ๋ฟ๋ง ์๋๋ผ ๋ฆฌ๋ ๋๋ง ๋๊ธฐ ์ ์ ํธ์ถ๋๋ค. update state๋ฅผ object๋ก ๋๋ ค์ค ์๋ ์๊ณ , ์๋ก์ด props๊ฐ state ์ ๋ฐ์ดํธ๋ฅผ ํ์ง ์์๋ ๋๋ ๊ฒ์ ๋ํ๋ด๊ธฐ ์ํดnull
์ ๋ฆฌํดํ ์ ์๋ค.class MyComponent extends React.Component { static getDerivedStateFromProps(props, state) { // ... } }
์ด ๋ผ์ดํ ์ฌ์ดํด ๋ฉ์๋๋
componentDidUpdate()
์componentWillReceiveProps()
์ ๋ชจ๋ ์ฌ์ฉ ์ฌ๋ก๋ฅผ ์ปค๋ฒํ๋ค. -
์๋ก์ด
getSnapshotBeforeUpdate()
๋ผ์ดํ ์ฌ์ดํด ๋ฉ์๋๋ DOM ์ ๋ฐ์ดํธ ์ง์ ์ ํธ์ถ๋๋ค. ์ด ๋ฉ์๋์ ๋ฐํ ๊ฐ์ ์ธ ๋ฒ์งธ ๋งค๊ฐ ๋ณ์๋กcomponentDidUpdate()
์ ์ ๋ฌ๋๋ค.class MyComponent extends React.Component { getSnapshotBeforeUpdate(prevProps, prevState) { // ... } }
์ด ๋ผ์ดํ ์ฌ์ดํด ๋ฉ์๋๋
componentDidUpdate()
์componentWillUpdate()
์ ๋ชจ๋ ์ฌ์ฉ ์ฌ๋ก๋ฅผ ์ปค๋ฒํ๋ค. -
๋ ๋๋ง props์ ๊ณ ์ฐจ์ ์ปดํฌ๋ํธ ๋ชจ๋ ํ๋์ ์์๋ง ๋ ๋๋งํ์ง๋ง, ๋๋ถ๋ถ์ ๊ฒฝ์ฐ Hooks๋ ํธ๋ฆฌ์์ ์ค์ฒฉ์ ์ค์์ผ๋ก์จ ์ ๊ฑฐํ๋ ๋ ๊ฐ๋จํ ๋ฐฉ๋ฒ์ด๋ค.
-
displayName
์ ์ฌ์ฉํ๋ ๋์ ์ฐธ์กฐ๋ก ์ปดํฌ๋ํธ์ ์ด๋ฆ์ ์ง์ ํ๋ ๊ฒ์ด ์ข๋ค.์ปดํฌ๋ํธ ๋ช ๋ช ์
displayName
๋ฅผ ์ฌ์ฉํ๋ ๊ฒฝ์ฐ.export default React.createClass({ displayName: 'TodoApp', // ... })
๊ถ์ฅ๋๋ ์ ๊ทผ ๋ฐฉ๋ฒ:
export default class TodoApp extends React.Component { // ... }
-
๋ง์ดํ ์์ ๋ ๋๋ง ๋จ๊ณ๊น์ง ๊ถ์ฅ๋๋ ๋ฉ์๋ ์์๋ ์๋์ ๊ฐ๋ค.
static
๋ฉ์๋constructor()
getChildContext()
componentWillMount()
componentDidMount()
componentWillReceiveProps()
shouldComponentUpdate()
componentWillUpdate()
componentDidUpdate()
componentWillUnmount()
onClickSubmit()
๋๋onChangeDescription()
๊ณผ ๊ฐ์ ํธ๋ค๋ฌ ๋๋ ์ด๋ฒคํธ ํธ๋ค๋ฌ๋ฅผ ํด๋ฆญ.getSelectReason()
๋๋getFooterContent()
์ ๊ฐ์ ๋ ๋๋ง์ ์ํ getter ๋ฉ์๋renderNavigation()
๋๋renderProfilePicture()
์ ๊ฐ์ ์ ํ์ ๋ ๋๋ง ๋ฉ์๋render()
-
์ค์์นญ ์ปดํฌ๋ํธ๋๋ ๋ง์ ์ปดํฌ๋ํธ ์ค ํ๋๋ฅผ ๋ ๋๋งํ๋ ์ปดํฌ๋ํธ์ด๋ค. prop ๊ฐ์ ์ปดํฌ๋ํธ์ ๋งคํํ๋ ค๋ฉด object๋ฅผ ์ฌ์ฉํด์ผ ํ๋ค.
์๋ฅผ ๋ค์ด,
page
prop์ ๊ธฐ๋ฐ์ผ๋ก ํ ๋ค๋ฅธ ํ์ด์ง๋ฅผ ํ์ํ๋ ์ค์์นญ ์ปดํฌ๋ํธ.import HomePage from './HomePage' import AboutPage from './AboutPage' import ServicesPage from './ServicesPage' import ContactPage from './ContactPage' const PAGES = { home: HomePage, about: AboutPage, services: ServicesPage, contact: ContactPage } const Page = (props) => { const Handler = PAGES[props.page] || ContactPage return <Handler {...props} /> } // PAGES ๊ฐ์ฒด์ ํค๋ prop ํ์ ์์ ์ฌ์ฉ๋์ด dev-time errors๋ฅผ ์ก์๋ผ ์ ์๋ค. Page.propTypes = { page: PropTypes.oneOf(Object.keys(PAGES)).isRequired }
-
setState()
๊ฐ ๋น๋๊ธฐ ์ฐ์ฐ์ด๊ธฐ ๋๋ฌธ์ด๋ค. ์ฑ๋ฅ์ ์ด์ ๋ก React๋ state๋ฅผ ์ผ๊ด์ ์ผ๋ก ๋ณ๊ฒฝํ๋ค. ๊ทธ๋์setState()
๊ฐ ํธ์ถ๋๊ณ state๊ฐ ์ฆ์ ๋ณ๊ฒฝ๋์ง ์๋๋ค. ์ฆsetState()
โ๋ฅผ ํธ์ถํ ๋ ํ์ฌ ์ํ์ ์์กดํด์๋ ์ ๋๋ฉฐ ๊ทธ ์ํ๊ฐ ๋ฌด์์ธ์ง ํ์ ํ ์ ์๊ฒ ๋๋ค. ํด๊ฒฐ๋ฐฉ๋ฒ์ ์ด์ ์ํ๋ฅผ ์ธ์๋ก ์ฌ์ฉํ๊ธฐ ์ํดsetState()
์ ํจ์๋ฅผ ์ ๋ฌํ๋ ๊ฒ์ด๋ค. ์ด๋ ๊ฒ ํ๋ฉดsetState()
์ ๋น๋๊ธฐ ํน์ฑ์ผ๋ก ์ธํด ์ฌ์ฉ์๊ฐ ์ก์ธ์คํ ๋ ์ด์ ์ํ ๊ฐ์ ๊ฐ์ ธ์ค๋ ๋ฌธ์ ๋ฅผ ํผํ ์ ์๋ค.์ด๊ธฐ count ๊ฐ์ 0์ด๋ผ๊ณ ๊ฐ์ ํด๋ณด์. ์ธ ๋ฒ์ ์ฐ์ ์ฆ๊ฐ ์ฐ์ฐ์ ํ๋ฉด, ๊ฐ์ 1๋ง ์ฆ๊ฐํ๋ค.
// assuming this.state.count === 0 this.setState({ count: this.state.count + 1 }) this.setState({ count: this.state.count + 1 }) this.setState({ count: this.state.count + 1 }) // this.state.count === 1, not 3
setState()
์ ํจ์๋ฅผ ์ ๋ฌํ๋ฉด, count๋ ์ฌ๋ฐ๋ฅด๊ฒ ์ฆ๊ฐํ๋ค.this.setState((prevState, props) => ({ count: prevState.count + props.increment })) // this.state.count === 3 as expected
-
React.StrictMode
๋ ์ ํ๋ฆฌ์ผ์ด์ ์ ์ ์ฌ์ ์ธ ๋ฌธ์ ์ ์ ๊ฐ์กฐ ํ์ํ๋๋ฐ ์ ์ฉํ ์ปดํฌ๋ํธ์ด๋ค.<Fragment>
์ ๋ง์ฐฌ๊ฐ์ง๋ก,<StrictMode>
๋ ํน์ DOM ์๋ฆฌ๋จผํธ์ ๋ ๋๋งํ์ง ์๋๋ค. ์์์ ๋ํ ์ถ๊ฐ ์ ๊ฒ๊ณผ ๊ฒฝ๊ณ ๋ฅผ ํ์ฑํํ๋ค. ์ด๋ฌํ ์ ๊ฒ์ ๊ฐ๋ฐ ๋ชจ๋์๋ง ์ ์ฉ์ด ๋๋ค.import React from 'react' function ExampleApplication() { return ( <div> <Header /> <React.StrictMode> <div> <ComponentOne /> <ComponentTwo /> </div> </React.StrictMode> <Footer /> </div> ) }
์์ ์์ ์์, strict mode ๊ฒ์ฌ๋
<ComponentOne>
์<ComponentTwo>
์ปดํฌ๋ํธ์๋ง ์ ์ฉ๋๋ค. -
Mixins์ ๊ณตํต ๊ธฐ๋ฅ์ ๊ฐ๋๋ก ์ปดํฌ๋ํธ๋ฅผ ์์ ํ ๋ถ๋ฆฌํ๋ ๋ฐฉ๋ฒ์ด๋ค. Mixins์ ์ฌ์ฉํ์ง ์์์ผ ํ๋ฉฐ ๊ณ ์ฐจ์ ์ปดํฌ๋ํธ๋ ๋ฐ์ฝ๋ ์ดํฐ๋ก ๋์ฒดํ ์ ์๋ค.
๊ฐ์ฅ ์ผ๋ฐ์ ์ผ๋ก ์ฌ์ฉ๋๋ mixins์
PureRenderMixin
์ด๋ค. props์ state๊ฐ ์ด์ props, state์ ์๊ฒ ๋๋ฑํ ๋ ๋ถํ์ํ ๋ฆฌ๋ ๋๋ง์ ๋ฐฉ์งํ๊ธฐ ์ํด ์ผ๋ถ ์ปดํฌ๋ํธ์์ ์ฌ์ฉํ ์๋ ์๋ค.const PureRenderMixin = require('react-addons-pure-render-mixin') const Button = React.createClass({ mixins: [PureRenderMixin], // ... })
-
isMounted()
์ ์ฃผ์ ์ฌ์ฉ ์ฌ๋ก๋ ์ปดํฌ๋ํธ๊ฐ ๋ง์ดํธ ํด์ ๋ ํ์setState()
๋ฅผ ํธ์ถํ์ง ์๋๋ก ๊ฒฝ๊ณ ํ๋ ๊ฒ์ด๋ค.if (this.isMounted()) { this.setState({...}) }
setState()
๋ฅผ ํธ์ถํ๊ธฐ ์ ์isMounted()
๋ฅผ ๊ฒ์ฌํ๋ฉด ๊ฒฝ๊ณ ๊ฐ ์ ๊ฑฐ๋์ง๋ง, ๊ฒฝ๊ณ ์ ๋ชฉ์ ๋ ์์ค๋๋ค. ์ปดํฌ๋ํธ๊ฐ ๋ง์ดํธ ํด์ ๋ ํ์ ์ฐธ์กฐ๋ฅผ ๋ณด์ ํ๊ณ ์๋ค๊ณ ์๊ฐํ๊ธฐ ๋๋ฌธ์isMounted()
๋ฅผ ์ฌ์ฉํ๋ ๊ฒ์ ์ฝ๋ ์ค๋ฉ์ด๋ค.์ต์ ์ ํด๊ฒฐ์ฑ ์ ์ปดํฌ๋ํธ๊ฐ ๋ง์ดํธ ํด์ ๋ ํ์
setState()
๊ฐ ํธ์ถ๋ ์ ์๋ ์์น๋ฅผ ์ฐพ์ ์์ ํ๋ ๊ฒ์ด๋ค. ์ด๋ฌํ ์ํฉ์ ์ปดํฌ๋ํธ๊ฐ ์ผ๋ถ ๋ฐ์ดํฐ๋ฅผ ๊ธฐ๋ค๋ฆฌ๋ฉฐ, ๋ฐ์ดํฐ๊ฐ ๋์ฐฉํ๊ธฐ ์ ์ ๋ง์ดํธ ํด์ ๋ ๋ ์ฝ๋ฐฑ์ผ๋ก ์ธํด ๊ฐ์ฅ ์ผ๋ฐ์ ์ผ๋ก ๋ฐ์ํ๋ค. ์ด์์ ์ผ๋ก, ์ฝ๋ฐฑ์ ๋ง์ดํธ ํด์ ์ ์componentWillUnmount()
์์ ์ทจ์ํด์ผ ํ๋ค. -
Pointer Events๋ ๋ชจ๋ ์ ๋ ฅ ์ด๋ฒคํธ๋ฅผ ์ฒ๋ฆฌํ๋ ํต์ผ๋ ๋ฐฉ๋ฒ์ ์ ๊ณตํ๋ค. ์์ ์๋ ๋ง์ฐ์ค์ ๊ฐ๊ฐ์ ์ด๋ฒคํธ ๋ฆฌ์ค๋๊ฐ ์์์ง๋ง, ์์ฆ์๋ ํฐ์น์คํฌ๋ฆฐ์ด๋ ํ์ด ๋ฌ๋ฆฐ ํด๋์ ํ์ ๊ฐ์ด ๋ง์ฐ์ค์ ๊ด๋ จ ์๋ ์ฅ์น๊ฐ ๋ง๋ค. ์ด๋ฌํ ์ด๋ฒคํธ๋ Pointer Events ์ฌ์์ ์ง์ํ๋ ๋ธ๋ผ์ฐ์ ์์๋ง ์๋ํ๋ค๋ ๊ฒ์ ๊ธฐ์ตํด์ผ ํ๋ค.
React DOM์์ ๋ค์ ์ด๋ฒคํธ ์ ํ์ ์ฌ์ฉํ ์ ์๋ค.
onPointerDown
onPointerMove
onPointerUp
onPointerCancel
onGotPointerCapture
onLostPointerCaptur
onPointerEnter
onPointerLeave
onPointerOver
onPointerOut
-
JSX๋ฅผ ์ฌ์ฉํ์ฌ ์ปดํฌ๋ํธ๋ฅผ ๋ ๋๋งํ๋ ๊ฒฝ์ฐ, ํด๋น ์ปดํฌ๋ํธ์ ์ด๋ฆ์ ๋๋ฌธ์๋ก ์์ํด์ผ ํ๋ค. ๊ทธ๋ ์ง ์์ผ๋ฉด React๊ฐ ์ธ์ํ ์ ์๋ ํ๊ทธ๋ก ์ค๋ฅ๋ฅผ ๋ฐ์์ํจ๋ค. ์ด ๊ท์น์ HTML ์์์ SVG ํ๊ทธ๋ง ์๋ฌธ์๋ก ์์ํ ์ ์๊ธฐ ๋๋ฌธ์ ์ฌ์ฉ๋๋ค.
์ด๋ฆ์ด ์๋ฌธ์๋ก ์์ํ๋ ์ปดํฌ๋ํธ ํด๋์ค๋ฅผ ์ ์ํ ์ ์์ง๋ง ๊ฐ์ ธ์ฌ ๋ ๋๋ฌธ์์ฌ์ผ ํ๋ค. ์๋์ ๊ฐ์ ์๋ฌธ์๋ ๊ด์ฐฎ๋ค.
class myComponent extends Component { render() { return <div /> } } export default myComponent
๋ค๋ฅธ ํ์ผ์ ๊ฐ์ ธ์ฌ ๋ ๋๋ฌธ์๋ก ์์ํด์ผ ํ๋ค.
import MyComponent from './MyComponent'
-
๊ทธ๋ ๋ค. ๊ณผ๊ฑฐ์ React๋ ์ ์ ์๋ DOM ์์ฑ์ ๋ฌด์ํ๊ณค ํ๋ค. React๊ฐ ์ธ์ํ์ง ๋ชปํ๋ ์์ฑ์ ๊ฐ์ง JSX๋ฅผ ์์ฑํ๋ค๋ฉด React๋ ๊ทธ๋ฅ ๊ฑด๋๋ธ ๊ฒ์ด๋ค. ์๋ฅผ ๋ค๋ฉด, ๋ค์๊ณผ ๊ฐ๋ค.
<div mycustomattribute={'something'} />
React v15๋ก ๋น div๋ฅผ DOM์ ๋ ๋๋งํ๋ค.
<div />
React v16์์๋ ์ ์ ์๋ ์์ฑ์ด DOM์ ์ ์ฅ๋๋ค.
<div mycustomattribute='something' />
์ด๊ฒ์ ํน์ ๋ธ๋ผ์ฐ์ ๋นํ์ค ์์ฑ์ ์ ๊ณตํ๊ณ , ์๋ก์ด DOM API๋ฅผ ์ฌ์ฉํ๊ณ , ๋ผ์ด๋ธ๋ฌ๋ฆฌ์ ํตํฉํ ๋ ์ ์ฉํ๋ค.
-
ES6์ ํด๋์ค๋ฅผ ์ฌ์ฉํ ๋๋ ์์ฑ์์์ ์ํ๋ฅผ ์ด๊ธฐํํ๊ณ
React.createClass()
๋ฅผ ์ฌ์ฉํ ๋๋getInitialState()
๋ฉ์๋๋ฅผ ์ด๊ธฐํํด์ผ ํ๋ค.ES6 ํด๋์ค ์ฌ์ฉ ์
class MyComponent extends React.Component { constructor(props) { super(props) this.state = { /* initial state */ } } }
React.createClass()
์ฌ์ฉ ์const MyComponent = React.createClass({ getInitialState() { return { /* initial state */ } } })
Note:
React.createClass()
๋ ๋ ์ด์ ์ฌ์ฉ๋์ง ์์ผ๋ฉฐ React v16์์ ์ ๊ฑฐ๋์๋ค. ๋์ ์ ์๋ฐ์คํฌ๋ฆฝํธ ํด๋์ค๋ฅผ ์ฌ์ฉํด์ผ ํ๋ค. -
๊ธฐ๋ณธ์ ์ผ๋ก, ์ปดํฌ๋ํธ์ state ๋๋ props๊ฐ ๋ณ๊ฒฝ๋๋ฉด ์ปดํฌ๋ํธ๊ฐ ๋ฆฌ๋ ๋๋ง์ด ๋๋ค.
render()
๋ฉ์๋๊ฐ ๋ค๋ฅธ ๋ฐ์ดํฐ์ ์์กดํ๋ ๊ฒฝ์ฐ,forceUpdate()
๋ฅผ ํธ์ถํ์ฌ ์ปดํฌ๋ํธ์ ๋ ๋๋ง์ ๋ค์ ํด์ผ ํ๋ค๋ ๊ฒ์ React์๊ฒ ์๋ฆด ์ ์๋ค.component.forceUpdate(callback)
forceUpdate()
์ ์ฌ์ฉ์ ํผํ๊ณrender()
์this.props
๊ณผthis.state
์์ ์ฝ๊ธฐ๋ง ํ๋ ๊ฒ์ด ์ข๋ค. -
constructor()
์์this.props
์ ์ ๊ทผํ๋ ค๋ฉด props๋ฅผsuper()
๋ฉ์๋์ ์ ๋ฌํด์ผ ํ๋ค.super(props)
์ฌ์ฉ ์:class MyComponent extends React.Component { constructor(props) { super(props) console.log(this.props) // { name: 'John', ... } } }
super()
์ฌ์ฉ ์:class MyComponent extends React.Component { constructor(props) { super() console.log(this.props) // undefined } }
constructor()
์ธ๋ถ์์๋this.props
์ ๋ํด์ ๊ฐ์ ๊ฐ์ ํ์ํ๋ค. -
ES6 ํ์ดํ ํจ์ ๊ตฌ๋ฌธ๊ณผ ํจ๊ป
Array.prototype.map
์ ๊ฐ๋จํ ์ฌ์ฉํ ์ ์๋ค. ์๋ฅผ ๋ค์ด, ๊ฐ์ฒด์items
๋ฐฐ์ด์ ๋ค์๊ณผ ๊ฐ์ ์ปดํฌ๋ํธ์ ๋ฐฐ์ด๊ณผ ๋งคํ๋๋ค.<tbody> {items.map(item => <SomeComponent key={item.id} name={item.name} />)} </tbody>
for
๋ฐ๋ณต๋ฌธ์ ์ฌ์ฉํ์ฌ ๋ฐ๋ณตํ ์ ์๋ค.<tbody> for (let i = 0; i < items.length; i++) { <SomeComponent key={items[i].id} name={items[i].name} /> } </tbody>
JSX ํ๊ทธ๊ฐ ํจ์ ํธ์ถ๋ก ๋ณํ๋์ด ํํ์์์ ๋ช ๋ น๋ฌธ์ ์ฌ์ฉํ ์ ์๊ธฐ ๋๋ฌธ์ด๋ค. ์ด๊ฒ์ stage 1 proposal์
์ฌ๋ผ๊ฐ ์๊ธฐ
๋๋ฌธ์ ๋ฐ๋์ ์๋ค. -
React (๋๋ JSX)๋ ์์ฑ๊ฐ ๋ด๋ถ์ ๋ณ์ ๋ณด๊ฐ์ ์ง์ํ์ง ์๋๋ค. ์๋์ ํํ์ ์๋ํ์ง ์๋๋ค.
<img className='image' src='images/{this.props.image}' />
๊ทธ๋ฌ๋ JS ํํ์์ ์ค๊ดํธ ์์ ์ ์ฒด ์์ฑ๊ฐ์ผ๋ก ๋ฃ์ ์ ์๋ค. ๋ฐ๋ผ์, ์๋ ํํ์์ด ์๋ํ๋ค.
<img className='image' src={'images/' + this.props.image} />
ํ ํ๋ฆฟ ๋ฌธ์์ด์ ์ฌ์ฉํ๋ฉด ๋ค์๊ณผ ๊ฐ์ด ์ฌ์ฉํ ์ ์๋ค.
<img className='image' src={`images/${this.props.image}`} />
-
ํน์ ๋ชจ์์ ์ปดํฌ๋ํธ์ ๊ฐ์ฒด ๋ฐฐ์ด์ ์ ๋ฌํ๋ ค๋ฉด
React.PropTypes.arrayOf()
์ ๋ํ ์ธ์๋กReact.PropTypes.shape()
๋ฅผ ์ฌ์ฉํ๋ค.ReactComponent.propTypes = { arrayWithShape: React.PropTypes.arrayOf(React.PropTypes.shape({ color: React.PropTypes.string.isRequired, fontSize: React.PropTypes.number.isRequired })).isRequired }
-
๋ฐ์ดํ ์์์ ์ค๊ดํธ๋ฅผ ์ฌ์ฉํ๋ฉด ๋ฌธ์์ด๋ก ํ๊ฐ๋๊ธฐ ๋๋ฌธ์ ์ค๊ดํธ๋ฅผ ์ฌ์ฉํ๋ฉด ์ ๋๋ค.
<div className="btn-panel {this.props.visible ? 'show' : 'hidden'}">
๋์ ์ค๊ดํธ๋ฅผ ๋ฐ๊นฅ์ผ๋ก ์ฎ๊ฒจ์ผ ํ๋ค. (ํด๋์ค ์ด๋ฆ๋ค ์ฌ์ด์ ๊ณต๋ฐฑ์ ๋ฃ๋ ๊ฒ์ ์์ง ๋ง์์ผ ํ๋ค.)
<div className={'btn-panel ' + (this.props.visible ? 'show' : 'hidden')}>
ํ ํ๋ฆฟ ๋ฌธ์์ด๋ ์๋ํ๋ค.
<div className={`btn-panel ${this.props.visible ? 'show' : 'hidden'}`}>
-
react
ํจํค์ง๋React.createElement()
,React.Component
,React.Children
, ์๋ฆฌ๋จผํธ ์ปดํฌ๋ํธ ๋ฐ ํด๋์ค์ ๊ด๋ จ๋ ๊ธฐํ ๋์ฐ๋ฏธ๊ฐ ํฌํจ๋์ด ์๋ค. ์ปดํฌ๋ํธ๋ฅผ ๋ง๋ค ๋ ํ์ํ ๋ํ ๋๋ ๋ณดํธ์ ์ธ ๋์ฐ๋ฏธ๋ผ๊ณ ์๊ฐํ ์ ์๋ค.react-dom
ํจํค์ง์๋ReactDOM.render()
๊ฐ ํฌํจ๋์ด ์๊ณ ,react-dom/server
์๋ReactDOMServer.renderToString()
๊ณผReactDOMServer.renderToStaticMarkup()
์ ์ฌ์ฉํ์ฌ ์๋ฒ ์ฌ์ด๋ ๋ ๋๋ง์ ์ง์ํ๋ค. -
React ํ์ ๋ชจ๋ DOM ๊ด๋ จ ๊ธฐ๋ฅ์ ReactDOM์ด๋ผ๋ ๋ณ๋์ ๋ผ์ด๋ธ๋ฌ๋ฆฌ๋ก ๋ถ๋ฆฌํ๋ค. React v0.14๋ ๋ผ์ด๋ธ๋ฌ๋ฆฌ๊ฐ ๋ถ๋ฆฌ๋ ์ฒซ ๋ฒ์งธ ๋ฆด๋ฆฌ์ฆ์ด๋ค.
react-native
,react-art
,react-canvas
,react-three
๊ฐ์ ์ผ๋ถ ํจํค์ง๋ฅผ ์ดํด๋ณด๋ฉด, React์ ์๋ฆ๋ค์๊ณผ ๋ณธ์ง์ ๋ธ๋ผ์ฐ์ ๋ DOM๊ณผ๋ ๊ด๋ จ์ด ์๋ค๋ ๊ฒ์ ๋ถ๋ช ํ ํ๋ค. React๊ฐ ๋ ๋๋งํ ์ ์๋ ๋ ๋ง์ ํ๊ฒฝ์ ๊ตฌ์ถํ๊ธฐ ์ํด, React ํ์ ์ฃผ React ํจํค์ง๋ฅผreact
์react-dom
๋ ๊ฐ๋ก ๋๋ ๊ณํ์ ์ธ์ ๋ค. ์ด๋ React๊ณผ React Native์ ์น ๋ฒ์ ๊ฐ์ ๊ณต์ ํ ์ ์๋ ์ปดํฌ๋ํธ๋ฅผ ์์ฑํ๋ ๊ธธ์ ์ด์ด์ฃผ์๋ค. -
ํ์ค
for
์์ฑ์ ์ฌ์ฉํ์ฌ ํ ์คํธ ์ ๋ ฅ์ ๋ฐ์ธ๋๋<label>
์๋ฆฌ๋จผํธ๋ฅผ ๋ ๋๋งํ๋ ค๊ณ ํ๋ฉด, ํด๋น ์์ฑ์ด ์๋ HTML์ด ์์ฑ๋๊ณ ์ฝ์์ ๊ฒฝ๊ณ ๊ฐ ์ธ์๋๋ค.<label for={'user'}>{'User'}</label> <input type={'text'} id={'user'} />
for
๋ JavaScript์์ ์์ฝ๋ ํค์๋์ด๋ฏ๋ก, ๋์htmlFor
์ ์ฌ์ฉํด์ผ ํ๋ค.<label htmlFor={'user'}>{'User'}</label> <input type={'text'} id={'user'} />
-
์ผ๋ฐ์ ์ธ React์์ ์ฌ์ฉํ๋ spread ์ฐ์ฐ์.
<button style={{...styles.panel.button, ...styles.panel.submitButton}}>{'Submit'}</button>
React Native๋ฅผ ์ฌ์ฉํ๋ค๋ฉด ๋ฐฐ์ด ํ๊ธฐ๋ฒ์ ์ฌ์ฉํ ์ ์๋ค.
<button style={[styles.panel.button, styles.panel.submitButton]}>{'Submit'}</button>
-
componentDidMount()
์์resize
์ด๋ฒคํธ๋ฅผ ์์ ํ๋ฉด, ํฌ๊ธฐ(๋๋น
๋ฐ๋์ด
)๋ฅผ ์ ๋ฐ์ดํธ ํ ์ ์๋ค.componentWillUnmount()
๋ฉ์๋์์ ๋ฆฌ์ค๋๋ฅผ ์ ๊ฑฐํด์ผ ํ๋ค.class WindowDimensions extends React.Component { constructor(props){ super(props); this.updateDimensions = this.updateDimensions.bind(this); } componentWillMount() { this.updateDimensions() } componentDidMount() { window.addEventListener('resize', this.updateDimensions) } componentWillUnmount() { window.removeEventListener('resize', this.updateDimensions) } updateDimensions() { this.setState({width: window.innerWidth, height: window.innerHeight}) } render() { return <span>{this.state.width} x {this.state.height}</span> } }
-
setState()
๋ฅผ ์ฌ์ฉํ๋ฉด ํ์ฌ state์ ์ด์ state๊ฐ ๋ณํฉ๋๋ค.replaceState()
๋ ํ์ฌ state๋ฅผ ๋ฒ๋ฆฌ๊ณ ์ฌ์ฉ์๊ฐ ๋ฃ์ state๋ก ๋ฐ๊พผ๋ค. Usually ์ด๋ค ์ด์ ๋ก ์ด์ ์ ๋ชจ๋ ํค๋ฅผ ์ ๊ฑฐํด์ผ ํ๋ ๊ฒฝ์ฐ๊ฐ ์๋๋ฉดsetState()
๋ฅผ ์ฌ์ฉํ๋ค.replaceState()
๋ฅผ ์ฌ์ฉํ๋ ๋์setState()
์์ state๋ฅผfalse
/null
๋ก ์ค์ ํ ์๋ ์๋ค. -
state๊ฐ ๋ณ๊ฒฝ๋๋ฉด ๋ค์์ ๋ผ์ดํ ์ฌ์ดํด ๋ฉ์๋๊ฐ ํธ์ถ๋๋ค. ์ ๊ณต๋ state์ props ๊ฐ์ ํ์ฌ state, props์ ๋น๊ตํ์ฌ ์๋ฏธ ์๋ ๊ฒ์ด ๋ณ๊ฒฝ๋์๋์ง ํ์ธํ ์ ์๋ค.
componentWillUpdate(object nextProps, object nextState) componentDidUpdate(object prevProps, object prevState)
-
๋ ๋์ ๋ฐฉ๋ฒ์
Array.prototype.filter()
๋ฉ์๋๋ฅผ ์ฌ์ฉํ๋ ๊ฒ์ด๋ค.์๋ฅผ ๋ค์ด, state๋ฅผ ์ ๋ฐ์ดํธํ๊ธฐ ์ํ
removeItem()
๋ฉ์๋๋ฅผ ์์ฑํด๋ณด์removeItem(index) { this.setState({ data: this.state.data.filter((item, i) => i !== index) }) }
-
์ต์ ๋ฒ์ (>=16.2)์์ ๊ฐ๋ฅํ๋ค. ๊ฐ๋ฅํ ์ต์ ์ ์๋์ ๊ฐ๋ค.
render() { return false }
render() { return null }
render() { return [] }
render() { return <React.Fragment></React.Fragment> }
render() { return <></> }
undefined
๋ฐํ์ ์๋ํ์ง ์๋๋ค -
<pre>
ํ๊ทธ๋ฅผ ์ฌ์ฉํ์ฌJSON.stringify()
์ ์์์ด ์ ์ง๋๋๋ก ํ ์ ์๋ค.const data = { name: 'John', age: 42 } class User extends React.Component { render() { return ( <pre> {JSON.stringify(data, null, 2)} </pre> ) } } React.render(<User />, document.getElementById('container'))
-
React ์ฒ ํ์ props๊ฐ ๋ถ๋ณ์ด๊ณ ํํฅ์์ด์ด์ผ ํ๋ค๋ ๊ฒ์ด๋ค. ์ฆ, ๋ถ๋ชจ๋ ๋ชจ๋ props ๊ฐ์ ์์์๊ฒ ๋ณด๋ผ ์ ์์ง๋ง, ์์์ ๋ฐ์ props๋ฅผ ์์ ํ ์ ์๋ค.
-
input
์๋ฆฌ๋จผํธ์ ๋ํ ref๋ฅผ ์์ฑํ๊ณcomponentDidMount()
์์ ์ด๋ฅผ ์ฌ์ฉํ๋ค.class App extends React.Component{ componentDidMount() { this.nameInput.focus() } render() { return ( <div> <input defaultValue={'Won\'t focus'} /> <input ref={(input) => this.nameInput = input} defaultValue={'Will focus'} /> </div> ) } } ReactDOM.render(<App />, document.getElementById('app'))
-
-
state์ ๋ณํฉ ํ ๊ฐ์ฒด๊ฐ ์๋
setState()
๋ฅผ ํธ์ถํ๋ค.-
Object.assign()
๋ฅผ ์ฌ์ฉํ์ฌ ๊ฐ์ฒด์ ๋ณต์ฌ๋ณธ์ ๋ง๋ ๋ค:const user = Object.assign({}, this.state.user, { age: 42 }) this.setState({ user })
-
spread ์ฐ์ฐ์๋ฅผ ์ฌ์ฉํ๋ค.
const user = { ...this.state.user, age: 42 } this.setState({ user })
-
-
setState()
๋ฅผ ํจ์์ ๊ฐ์ด ์ฌ์ฉํ๋ค.this.setState(prevState => ({ user: { ...prevState.user, age: 42 } }))
-
-
React๋ ์ฌ๋ฌ
setState()
ํธ์ถ์ ์ฑ๋ฅ ํฅ์์ ์ํด ๋จ์ผ ์ ๋ฐ์ดํธ๋ก ์ผ๊ด์ฒ๋ฆฌํ๋ค.this.props
์this.state
๊ฐ ๋น๋๊ธฐ์ ์ผ๋ก ์ ๋ฐ์ดํธ๋ ์ ์์์ผ๋ก ๋ค์ state๋ฅผ ๊ณ์ฐํ ๋ ํด๋น ๊ฐ์ ์ ๋ขฐํด์๋ ์ ๋๋ค.counter ์์ ๋ ์์๋๋ก ์ ๋ฐ์ดํธ๋์ง ์๋๋ค.
// Wrong this.setState({ counter: this.state.counter + this.props.increment, })
์ ํธ๋๋ ์ ๊ทผ๋ฒ์ ๊ฐ์ฒด๊ฐ ์๋ ํจ์๋ก
setState()
๋ฅผ ํธ์ถํ๋ ๊ฒ์ด๋ค. ์ด ํจ์๋ ์ฒซ ๋ฒ์งธ ์ธ์๋ก ์ด์ state๋ฅผ ๋ฐ๊ณ ๋ ๋ฒ์งธ ์ธ์๋ก ์ ๋ฐ์ดํธ๊ฐ ์ ์ฉ๋ props๋ฅผ ๋ฐ๋๋ค.// Correct this.setState((prevState, props) => ({ counter: prevState.counter + props.increment }))
-
React.version
์ ์ฌ์ฉํด์ ๋ฒ์ ์ ์ป์ ์ ์๋ค.const REACT_VERSION = React.version ReactDOM.render( <div>{`React version: ${REACT_VERSION}`}</div>, document.getElementById('app') )
-
-
core-js
์์ ์๋์ผ๋ก ๊ฐ์ ธ์ค๊ธฐpolyfills.js
์ ๊ฐ์ ํ์ผ์ ๋ง๋ค๊ณ rootindex.js
ํ์ผ์์ importํ๋ค.npm install core-js
๋๋yarn add core-js
๋ฅผ ์คํํ๊ณ ํน์ ํ์ ๊ธฐ๋ฅ์ importํ๋ค.import 'core-js/fn/array/find' import 'core-js/fn/array/includes' import 'core-js/fn/number/is-nan'
-
Polyfill ์๋น์ค๋ฅผ ์ฌ์ฉํ๊ธฐ:
์ด ์ค์
index.html
์ ์ถ๊ฐ, polyfill.io CDN์ ์ฌ์ฉํ์ฌ ์ฌ์ฉ์ ์ง์ ๋ธ๋ผ์ฐ์ ์ ์ฉ polyfill์ ๊ฒ์ํ๋ค.<script src='https://cdn.polyfill.io/v2/polyfill.min.js?features=default,Array.prototype.includes'></script>
์์ ์คํฌ๋ฆฝํธ์์
Array.prototype.includes
๊ธฐ๋ฅ์ ๊ธฐ๋ณธ ๊ธฐ๋ฅ ์งํฉ์ ํฌํจ๋์ด ์์ง ์์์ผ๋ก ๋ช ์์ ์ผ๋ก ์์ฒญํด์ผ ํ๋ค.
-
-
HTTPS=true
์ค์ ๋ง ์ฌ์ฉํ๋ฉด ๋๋ค.package.json
์คํฌ๋ฆฝํธ ์น์ ์ ํธ์งํ ์ ์๋ค."scripts": { "start": "set HTTPS=true && react-scripts start" }
๋๋
set HTTPS=true && npm start
๋ฅผ ์คํํ๋ฉด ๋๋ค. -
ํ๋ก์ ํธ ๋ฃจํธ์
.env
๋ผ๋ ํ์ผ์ ๋ง๋ค๊ณ ๊ฒฝ๋ก๋ฅผ import ํ๋ฉด ๋๋ค.NODE_PATH=src/app
๊ฐ๋ฐ ์๋ฒ๋ฅผ ๋ค์ ์์ํ๋ฉด, ์๋ ๊ฒฝ๋ก ์์ด
src/app
๋ด๋ถ์ ๋ด์ฉ์ import ํ ์ ์๋ค. -
history
๊ฐ์ฒด์ ๋ฆฌ์ค๋๋ฅผ ์ถ๊ฐํ์ฌ ๊ฐ๊ฐ์ ํ์ด์ง view๋ฅผ ๊ธฐ๋กํ๋ค.history.listen(function (location) { window.ga('set', 'page', location.pathname + location.search) window.ga('send', 'pageview', location.pathname + location.search) })
-
๋ณ๊ฒฝ์ ๊ฐ์งํ๋ ค๋ฉด
setInterval()
์ ์ฌ์ฉํด์ผ ํ์ง๋ง ์ค๋ฅ ๋ฐ ๋ฉ๋ชจ๋ฆฌ ๋์๋ฅผ ๋ฐฉ์งํ๋ ค๋ฉด ์ปดํฌ๋ํธ๊ฐ ๋ง์ดํธ ํด์ ๋ ๋ ํ์ด๋จธ๋ฅผ ์ง์์ผ ํ๋ค.componentDidMount() { this.interval = setInterval(() => this.setState({ time: Date.now() }), 1000) } componentWillUnmount() { clearInterval(this.interval) }
-
React๋ ๋ฒค๋ ์ ๋์ฌ๋ฅผ ์๋์ผ๋ก ์ ์ฉํ์ง ์๋๋ค. ๋ฒค๋ ์ ๋์ฌ๋ฅผ ์๋์ผ๋ก ์ถ๊ฐํด์ผ ํ๋ค.
<div style={{ transform: 'rotate(90deg)', WebkitTransform: 'rotate(90deg)', // note the capital 'W' here msTransform: 'rotate(90deg)' // 'ms' is the only lowercase vendor prefix }} />
-
์ปดํฌ๋ํธ๋ฅผ ๋ด๋ณด๋ด๋ ค๋ฉด default๋ฅผ ์ฌ์ฉํ๋ฉด ๋๋ค.
import React from 'react' import User from 'user' export default class MyProfile extends React.Component { render(){ return ( <User type="customer"> //... </User> ) } }
export ์ง์ ์๋ฅผ ์ฌ์ฉํ๋ฉด MyProfile์ด ๋ฉค๋ฒ๊ฐ ๋์ด ์ด ๋ชจ๋๋ก ๋ด๋ณด๋ผ ์ ์์ผ๋ฉฐ, ๋ค๋ฅธ ์ปดํฌ๋ํธ์ ์ด๋ฆ์ ์ธ๊ธํ์ง ์๊ณ ๋ ๊ฐ์ ธ์ฌ ์ ์๋ค.
-
JSX์์ ์๋ฌธ์ ํ๊ทธ ์ด๋ฆ์ HTML ํ๊ทธ๋ก ๊ฐ์ฃผํ๋ค. ๊ทธ๋ฌ๋ ์ ์ผ๋ก ํ์๋ ๋๋ฌธ์ ๋ฐ ์๋ฌธ์ ํ๊ทธ ์ด๋ฆ(์์ฑ ์ ๊ทผ์)์ ์๋ค.
<component />
๋React.createElement('component')
๋ก ์ปดํ์ผ๋๋ค. (i.e, HTML tag)<obj.component />
๋React.createElement(obj.component)
๋ก ์ปดํ์ผ๋๋ค.<Component />
๋React.createElement(Component)
๋ก ์ปดํ์ผ๋๋ค.
-
React์ reconciliation ์๊ณ ๋ฆฌ์ฆ์์๋ ์ ๋ฐ๋ ์ ๋ณด๊ฐ ์๋ค๊ณ ๊ฐ์ ํ๋๋ฐ, ์ฌ์ฉ์ ์ง์ ์ปดํฌ๋ํธ๊ฐ ํ์ ๋ ๋๋ง์ ๊ฐ์ ์์น์ ๋ํ๋๋ฉด, ์ด์ ์ปดํฌ๋ํธ์ ๋์ผ ํ๋ฏ๋ก ์ด์ ์ธ์คํด์ค๋ฅผ ์๋ก ์์ฑํ์ง ์๊ณ ๋ค์ ์ฌ์ฉํ๋ค.
-
ES7
static
ํ๋๋ฅผ ์ฌ์ฉํ์ฌ ์์๋ฅผ ์ ์ํ ์ ์๋ค.class MyComponent extends React.Component { static DEFAULT_PAGINATION = 10 }
Static fields๋ Class Fields ์คํ ์ด์ง 3 ์ ์์ ์ผ๋ถ์ด๋ค.
-
ref prop๋ฅผ ์ฌ์ฉํ์ฌ ์ฝ๋ฐฑ์ ํตํด ๊ธฐ๋ณธ
HTMLInputElement
๊ฐ์ฒด์ ๋ํ ์ฐธ์กฐ๋ฅผ ๊ฐ์ ธ์์, ์ฐธ์กฐ๋ฅผ ํด๋์ค ์์ฑ์ผ๋ก ์ ์ฅํ ๋ค์, ํด๋น ์ฐธ์กฐ์HTMLElement.click
๋ฉ์๋๋ฅผ ์ฌ์ฉํ์ฌ ์ด๋ฒคํธ ํธ๋ค๋ฌ์์ ํด๋ฆญ์ ํธ๋ฆฌ๊ฑฐ ํ ์ ์๋ค.-
render ๋ฉ์๋ ์์์ ref๋ฅผ ๋ง๋ ๋ค.
<input ref={input => this.inputElement = input} />
-
์ด๋ฒคํธ ํธ๋ค๋ฌ์ ํด๋ฆญ ์ด๋ฒคํธ๋ฅผ ์ ์ฉํ๋ค.
this.inputElement.click()
-
-
React์์
async
/await
์ ์ฌ์ฉํ๋ ค๋ฉด, Babel ๋ฐ transform-async-to-generator ํ๋ฌ๊ทธ์ธ์ด ํ์ํ๋ค. React Native๋ Babel๊ณผ ๋ณ๊ฒฝ ์ธํธ๋ฅผ ๊ตฌ์ฑํด์ผ ํ๋ค. -
React ํ๋ก์ ํธ ํ์ผ ๊ตฌ์กฐ๋ ์ผ๋ฐ์ ์ผ๋ก ์ฌ์ฉ๋๋ 2๊ฐ์ง ๋ฐฉ๋ฒ์ด ์๋ค.
-
๊ธฐ๋ฅ ๋๋ ๊ฒฝ๋ก๋ณ๋ก ๊ทธ๋ฃนํ
ํ๋ก์ ํธ๋ฅผ ๊ตฌ์กฐํํ๋ ์ผ๋ฐ์ ์ธ ๋ฐฉ๋ฒ์ ํ๋๋ ๊ธฐ๋ฅ์ด๋ ๊ฒฝ๋ก๋ณ๋ก ๊ทธ๋ฃนํ๋ CSS, JS, ๋ฐ ํ ์คํธ๋ฅผ ํจ๊ป ๋ฐฐ์นํ๋ ๊ฒ์ด๋ค.
common/ โโ Avatar.js โโ Avatar.css โโ APIUtils.js โโ APIUtils.test.js feed/ โโ index.js โโ Feed.js โโ Feed.css โโ FeedStory.js โโ FeedStory.test.js โโ FeedAPI.js profile/ โโ index.js โโ Profile.js โโ ProfileHeader.js โโ ProfileHeader.css โโ ProfileAPI.js
-
ํ์ผ ํ์ ์ผ๋ก ๊ทธ๋ฃนํ
ํ๋ก์ ํธ๋ฅผ ๊ตฌ์กฐํํ๋ ๋ค๋ฅธ ๋ณดํธ์ ์ธ ๋ฐฉ๋ฒ์ ์ ์ฌํ ํ์ผ๋ผ๋ฆฌ ๊ทธ๋ฃนํํ๋ ๊ฒ์ด๋ค.
api/ โโ APIUtils.js โโ APIUtils.test.js โโ ProfileAPI.js โโ UserAPI.js components/ โโ Avatar.js โโ Avatar.css โโ Feed.js โโ Feed.css โโ FeedStory.js โโ FeedStory.test.js โโ Profile.js โโ ProfileHeader.js โโ ProfileHeader.css
-
-
React Transition Group๊ณผ React Motion์ด React ์ํ๊ณ์์ ๊ฐ์ฅ ์ธ๊ธฐ ์๋ ์ ๋๋ฉ์ด์ ํจํค์ง์ด๋ค.
-
์ปดํฌ๋ํธ์์ ์คํ์ผ ๊ฐ์ ํ๋ ์ฝ๋ฉ ํ๋ ๊ฒ์ ์ข์ง ์๋ค. ๋ค๋ฅธ UI ์ปดํฌ๋ํธ์์ ์ฌ์ฉ๋ ๊ฐ๋ฅ์ฑ์ด ์๋ ๊ฐ์ ์์ฒด ๋ชจ๋๋ก ๋ฝ์๋ด์ผ ํ๋ค.
์๋ฅผ ๋ค์ด, ์ด๋ฌํ ์คํ์ผ์ ๋ณ๋์ ์ปดํฌ๋ํธ๋ก ๋ฝ์๋ผ ์ ์๋ค.
export const colors = { white, black, blue } export const space = [ 0, 8, 16, 32, 64 ]
๊ทธ๋ฐ ๋ค์ ๋ค๋ฅธ ์ปดํฌ๋ํธ์์ ๊ฐ๋ณ์ ์ผ๋ก ๊ฐ์ ธ์จ๋ค.
import { space, colors } from './styles'
-
ESLint๋ ์ธ๊ธฐ ์๋ JavaScript linter์ด๋ค. ํน์ ์ฝ๋ ์คํ์ผ์ ๋ถ์ํ ์ ์๋ ํ๋ฌ๊ทธ์ธ์ด ์๋ค. React์์ ๊ฐ์ฅ ์ผ๋ฐ์ ์ผ๋ก ์ฌ์ฉ๋๋ ํจํค์ง ์ค ํ๋๋
eslint-plugin-react
npm ํจํค์ง๋ค. ๊ธฐ๋ณธ์ ์ผ๋ก, ๋ฐ๋ณต์์๊ฒ ์๋ ํค๋ถํฐ ์ ์ฒด prop ํ์ ๊น์ง ๊ฒ์ฌํ๋ ๊ท์น์ ์ฌ์ฉํ์ฌ ๊ฐ์ฅ ์ข์ ์ฌ๋ก๋ฅผ ์ฌ๋ฌ ๋ฒ ํ์ธํ ๊ฒ์ด๋ค. ๋ ๋ค๋ฅธ ์ธ๊ธฐ ์๋ ํ๋ฌ๊ทธ์ธ์eslint-plugin-jsx-a11y
๋ก, ์ ๊ทผ์ฑ๊ณผ ๊ด๋ จ๋ ์ผ๋ฐ์ ์ธ ๋ฌธ์ ๋ฅผ ํด๊ฒฐํ๋ ๋ฐ ๋์์ด ๋๋ค. JSX๋ ์ผ๋ฐ HTML๊ณผ ์ฝ๊ฐ ๋ค๋ฅธ ๊ตฌ๋ฌธ์ ์ ๊ณตํ๊ธฐ ๋๋ฌธ์alt
ํ ์คํธ๋tabindex
์ ๊ด๋ จ๋ ๋ฌธ์ ๋ ์ผ๋ฐ์ ์ธ ํ๋ฌ๊ทธ์ธ์ผ๋ก ์ฐพ์๋ผ ์ ์๋ค. -
AJAX ํธ์ถํ๋ ๋ฐฉ๋ฒ๊ณผ ์ด๋ ์ปดํฌ๋ํธ ๋ผ์ดํ ์ฌ์ดํด ๋ฉ์๋์์ AJAX ํธ์ถ์ ํด์ผํ๋?
Axios, jQuery AJAX ๋ฐ ๋ธ๋ผ์ฐ์ ๋ด์ฅ
fetch
์ ๊ฐ์ AJAX libraries๋ฅผ ์ฌ์ฉํ ์ ์๋ค.componentDidMount()
๋ผ์ดํ ์ฌ์ดํด ๋ฉ์๋์์ ๋ฐ์ดํฐ๋ฅผ ๊ฐ์ ธ์์ผ ํ๋ค. ๋ฐ์ดํฐ๋ฅผ ๊ฒ์ํ ๋setState()
๋ฅผ ์ฌ์ฉํ์ฌ ์ปดํฌ๋ํธ๋ฅผ ์ ๋ฐ์ดํธ ํ ์ ์๋ค.์๋ฅผ ๋ค์ด, ์ง์ ๋ชฉ๋ก์ API์์ ๊ฐ์ ธ์์ ๋ก์ปฌ state์ ์ค์ ํ๋ค.
class MyComponent extends React.Component { constructor(props) { super(props) this.state = { employees: [], error: null } } componentDidMount() { fetch('https://api.example.com/items') .then(res => res.json()) .then( (result) => { this.setState({ employees: result.employees }) }, (error) => { this.setState({ error }) } ) } render() { const { error, employees } = this.state if (error) { return <div>Error: {error.message}</div>; } else { return ( <ul> {employees.map(item => ( <li key={employee.name}> {employee.name}-{employees.experience} </li> ))} </ul> ) } } }
-
Render Props๋ ๊ฐ์ด ํจ์์ธ prop์ ์ฌ์ฉํ์ฌ ์ปดํฌ๋ํธ ๊ฐ ์ฝ๋๋ฅผ ๊ณต์ ํ๋ ๊ฐ๋จํ ๊ธฐ์ ์ด๋ค. ์๋์ ์ปดํฌ๋ํธ๋ React ์๋ฆฌ๋จผํธ๋ฅผ ๋ฐํํ๋ render props๋ฅผ ์ฌ์ฉํ๋ค.
<DataProvider render={data => ( <h1>{`Hello ${data.target}`}</h1> )}/>
React Router์ DownShift ๊ฐ์ ๋ผ์ด๋ธ๋ฌ๋ฆฌ๋ ์ด ํจํด์ ์ฌ์ฉํ๋ค.
-
React Router๋ React ์์ ๊ตฌ์ถ๋ ๊ฐ๋ ฅํ ๋ผ์ฐํ ๋ผ์ด๋ธ๋ฌ๋ฆฌ๋ก URL์ ํ์ด์ง์ ํ์๋ ๋ด์ฉ๊ณผ ๋๊ธฐํ๋ ์ํ๋ก ์ ์งํ๋ฉด์ ์ ํ๋ฆฌ์ผ์ด์ ์ ์๋ก์ด ํ๋ฉด๊ณผ ํ๋ก์ฐ๋ฅผ ์ถ๊ฐํ ์ ์๋ค.
-
React Router๋ ๋ธ๋ผ์ฐ์ ์
window.history
์ ๋ธ๋ผ์ฐ์ ํด์ฌ ๊ธฐ๋ก์ ์ํธ ์์ฉ์ ์ฒ๋ฆฌํ๋history
๋ผ์ด๋ธ๋ฌ๋ฆฌ๋ฅผ ๊ฐ์ธ๋ ๋ํผ์ด๋ค. ๋ํ ๋ชจ๋ฐ์ผ ํ์คํ ๋ฆฌ๋ฅผ ์ ๊ณตํ์ฌ ๋ชจ๋ฐ์ผ ์ฑ ๊ฐ๋ฐ(React Native) ๋ฐ Node ๋จ์ ํ ์คํธ์ ๊ฐ์ด ๊ธ๋ก๋ฒ ํ์คํ ๋ฆฌ๊ฐ ์๋ ํ๊ฒฝ์ ์ ์ฉํ๋ค. -
React Router v4๋ ์๋์ 3๊ฐ์
<Router>
์ปดํฌ๋ํธ๋ฅผ ์ ๊ณตํ๋ค.<BrowserRouter>
<HashRouter>
<MemoryRouter>
์์ ์ปดํฌ๋ํธ๋ browser, hash ๋ฐ memory history ์ธ์คํด์ค๋ฅผ ์์ฑํ๋ค. React Router v4๋
router
๊ฐ์ฒด์ ์ปจํ ์คํธ๋ฅผ ํตํด์ ์ฌ์ฉ ๊ฐ๋ฅํ ๋ผ์ฐํฐ์ ์ฐ๊ฒฐ๋history
์ธ์คํด์ค์ ์์ฑ๊ณผ ๋ฉ์๋๋ฅผ ์ฌ์ฉํ ์ ์๋๋ก ํ๋ค. -
history ์ธ์คํด์ค์๋ ํ์์ ์ํ ๋๊ฐ์ง ๋ฉ์๋๊ฐ ์๋ค.
push()
replace()
history๋ฅผ ๋ฐฉ๋ฌธํ ์์น์ ๋ฐฐ์ด๋ก ์๊ฐํ๋ฉด
push()
๋ ๋ฐฐ์ด์ ์ ์์น๋ฅผ ์ถ๊ฐํ๊ณ ,replace()
๋ ๋ฐฐ์ด์ ํ์ฌ ์์น๋ฅผ ์๋ก์ด ์์น๋ก ๋ฐ๊พผ๋ค. -
์ปดํฌ๋ํธ๋ด์์ ํ๋ก๊ทธ๋๋ฐ ๋ฐฉ์์ ๋ผ์ฐํ / ํ์์ ์ํํ๋ ์ธ ๊ฐ์ง ๋ฐฉ๋ฒ์ด ์๋ค.
-
withRouter()
๊ณ ์ฐจ ํจ์ ์ฌ์ฉwithRouter()
๊ณ ์ฐจ ํจ์๋ history ๊ฐ์ฒด๋ฅผ ์ปดํฌ๋ํธ์ prop๋ก ์ฝ์ ๋๋ค. ์ด ๊ฐ์ฒด๋push()
๋ฐreplace()
๋ฉ์๋๋ฅผ ์ ๊ณตํ๋ค.import { withRouter } from 'react-router-dom' // this also works with 'react-router-native' const Button = withRouter(({ history }) => ( <button type='button' onClick={() => { history.push('/new-location') }} > {'Click Me!'} </button> ))
-
<Route>
์ปดํฌ๋ํธ์ ๋ ๋๋ง props ํจํด ์ฌ์ฉThe
<Route>
์ปดํฌ๋ํธ๋withRouter()
์ ๊ฐ์ prop์ ์ ๋ฌํ๋ฏ๋ก, history prop์ ํตํด history ๋ฉ์๋์ ์ ๊ทผํ ์ ์๋ค.import { Route } from 'react-router-dom' const Button = () => ( <Route render={({ history }) => ( <button type='button' onClick={() => { history.push('/new-location') }} > {'Click Me!'} </button> )} /> )
-
context ์ฌ์ฉ
์ด ์ต์ ์ ๊ถ์ฅ๋์ง ์์ผ๋ฉฐ ๋ถ์์ ํ API๋ก ์ฒ๋ฆฌ๋๋ค.
const Button = (props, context) => ( <button type='button' onClick={() => { context.history.push('/new-location') }} > {'Click Me!'} </button> ) Button.contextTypes = { history: React.PropTypes.shape({ push: React.PropTypes.func.isRequired }) }
-
-
๋ค๋ฅธ ๊ตฌํ์ ์ง์ํ๊ธฐ ์ํด ์๋ ๋์ ์ฌ์ฉ์ ์์ฒญ์ด ์์๊ธฐ ๋๋ฌธ์ ์ฟผ๋ฆฌ ๋ฌธ์์ด์ ๊ตฌ๋ฌธ ๋ถ์ํ๋ ๊ธฐ๋ฅ์ React Router v4์์ ์ ๊ฑฐ๋์๋ค. ๊ทธ๋์ ์ฌ์ฉ์๊ฐ ์ํ๋ ๊ตฌํ์ ์ ํํ๋๋ก ๊ฒฐ์ ๋์๋ค. ๊ถ์ฅ๋๋ ๋ฐฉ๋ฒ์ ์ฟผ๋ฆฌ ๋ฌธ์์ด ๋ผ์ด๋ธ๋ฌ๋ฆฌ๋ฅผ ์ฌ์ฉํ๋ ๊ฒ์ด๋ค.
const queryString = require('query-string'); const parsed = queryString.parse(props.location.search);
๋ค์ดํฐ๋ธ๋ฅผ ์ํ๋ค๋ฉด
URLSearchParams
๋ฅผ ์ฌ์ฉํ ์๋ ์๋ค.const params = new URLSearchParams(props.location.search) const foo = params.get('name')
IE11์์๋ polyfill๋ฅผ ์ฌ์ฉํด์ผํ๋ค.
-
<Switch>
๋ ๋ผ์ฐํฐ๋ฅผ ๋ ์ ์ ์ผ๋ก ๋ ๋๋งํ๋ค๋ ์ ์์ ๊ณ ์ ํ๋ฏ๋ก<Switch>
๋ธ๋ก์ ๋ผ์ฐํฐ๋ฅผ ๊ฐ์ธ์ผ ํ๋ค.๋จผ์
Switch
๋ฅผ import ํด์ค๋ค.import { Switch, Router, Route } from 'react-router'
๊ทธ๋ฐ ๋ค์
<Switch>
๋ธ๋ก ๋ด์์ ๊ฒฝ๋ก๋ฅผ ์ ์ํ๋ค.<Router> <Switch> <Route {/* ... */} /> <Route {/* ... */} /> </Switch> </Router>
-
๋ค๋น๊ฒ์ด์ ํ๋ ๋์
history
๊ฐ์ฒด์ props๋ฅผ ์ ๋ฌํ ์ ์๋ค.this.props.history.push({ pathname: '/template', search: '?name=sudheer', state: { detail: response.data } })
search
์์ฑ์push()
๋ฉ์๋์์ ์ฟผ๋ฆฌ ๋งค๊ฐ๋ณ์๋ฅผ ์ ๋ฌํ๋๋ฐ ์ฌ์ฉ๋๋ค. -
<Switch>
๋ ์ผ์นํ๋ ์ฒซ ๋ฒ์งธ ํ์<Route>
๋ฅผ ๋ ๋๋งํ๋ค. ๊ฒฝ๋ก๊ฐ ์๋<Route>
๋ ํญ์ ์ผ์นํ๋ค. ๋ฐ๋ผ์ ์๋์ ๊ฐ์ ๊ฒฝ๋ก ์์ฑ์ ์ญ์ ํ๋ฉด ๋๋ค.<Switch> <Route exact path="/" component={Home}/> <Route path="/user" component={User}/> <Route component={NotFound} /> </Switch>
-
-
history
๊ฐ์ฒด๋ฅผ export ํ ํ ํ๋ก์ ํธ ์ ์ฒด๋ฅผ import ํ๋ ๋ชจ๋์ ์์ฑํ๋ฉด ๋๋ค.์๋ฅผ ๋ค์ด,
history.js
ํ์ผ์ ๋ง๋ ๋ค.import { createBrowserHistory } from 'history' export default createBrowserHistory({ /* pass a configuration object here if needed */ })
-
๊ธฐ๋ณธ์ผ๋ก ์ ๊ณต๋๋ ๋ผ์ฐํฐ ๋์
<Router>
์ปดํฌ๋ํธ๋ฅผ ์ฌ์ฉํด์ผ ํ๋ค.index.js
ํ์ผ ๋ด์์history.js
๋ฅผ import ํ๋ค.import { Router } from 'react-router-dom' import history from './history' import App from './App' ReactDOM.render(( <Router history={history}> <App /> </Router> ), holder)
-
๋ด์ฅ๋ history ๊ฐ์ฒด์ ์ ์ฌํ
history
๊ฐ์ฒด์ push ๋ฉ์๋๋ฅผ ์ฌ์ฉํ ์ ์๋ค.// some-other-file.js import history from './history' history.push('/go-here')
-
-
react-router
ํจํค์ง๋ React Router์์<Redirect>
์ปดํฌ๋ํธ๋ฅผ ์ ๊ณตํ๋ค.<Redirect>
์ ๋ ๋๋งํ๋ฉด ์๋ก์ด ์์น๋ก ์ด๋ํ๋ค. ์๋ฒ ์ฌ์ด๋ ๋ฆฌ๋๋ ์ ๊ณผ ๋ง์ฐฌ๊ฐ์ง๋ก ์๋ก์ด ์์น๋ history ์คํ์ ํ์ฌ ์์น๋ฅผ ๋ฌด์ํ๋ค.import React, { Component } from 'react' import { Redirect } from 'react-router' export default class LoginComponent extends Component { render() { if (this.state.isLoggedIn === true) { return <Redirect to="/your/redirect/page" /> } else { return <div>{'Login Please'}</div> } } }
-
The React Intl library makes internalization in React straightforward, with off-the-shelf components and an API that can handle everything from formatting strings, dates, and numbers, to pluralization. React Intl is part of FormatJS which provides bindings to React via its components and API.
-
- Display numbers with separators.
- Display dates and times correctly.
- Display dates relative to "now".
- Pluralize labels in strings.
- Support for 150+ languages.
- Runs in the browser and Node.
- Built on standards.
-
The library provides two ways to format strings, numbers, and dates: react components or an API.
<FormattedMessage id={'account'} defaultMessage={'The amount is less than minimum balance.'} />
const messages = defineMessages({ accountMessage: { id: 'account', defaultMessage: 'The amount is less than minimum balance.', } }) formatMessage(messages.accountMessage)
-
The
<Formatted... />
components fromreact-intl
return elements, not plain text, so they can't be used for placeholders, alt text, etc. In that case, you should use lower level APIformatMessage()
. You can inject theintl
object into your component usinginjectIntl()
higher-order component and then format the message usingformatMessage()
available on that object.import React from 'react' import { injectIntl, intlShape } from 'react-intl' const MyComponent = ({ intl }) => { const placeholder = intl.formatMessage({id: 'messageId'}) return <input placeholder={placeholder} /> } MyComponent.propTypes = { intl: intlShape.isRequired } export default injectIntl(MyComponent)
-
You can get the current locale in any component of your application using
injectIntl()
:import { injectIntl, intlShape } from 'react-intl' const MyComponent = ({ intl }) => ( <div>{`The current locale is ${intl.locale}`}</div> ) MyComponent.propTypes = { intl: intlShape.isRequired } export default injectIntl(MyComponent)
-
The
injectIntl()
higher-order component will give you access to theformatDate()
method via the props in your component. The method is used internally by instances ofFormattedDate
and it returns the string representation of the formatted date.import { injectIntl, intlShape } from 'react-intl' const stringDate = this.props.intl.formatDate(date, { year: 'numeric', month: 'numeric', day: 'numeric' }) const MyComponent = ({intl}) => ( <div>{`The formatted date is ${stringDate}`}</div> ) MyComponent.propTypes = { intl: intlShape.isRequired } export default injectIntl(MyComponent)
-
Shallow rendering is useful for writing unit test cases in React. It lets you render a component one level deep and assert facts about what its render method returns, without worrying about the behavior of child components, which are not instantiated or rendered.
For example, if you have the following component:
function MyComponent() { return ( <div> <span className={'heading'}>{'Title'}</span> <span className={'description'}>{'Description'}</span> </div> ) }
Then you can assert as follows:
import ShallowRenderer from 'react-test-renderer/shallow' // in your test const renderer = new ShallowRenderer() renderer.render(<MyComponent />) const result = renderer.getRenderOutput() expect(result.type).toBe('div') expect(result.props.children).toEqual([ <span className={'heading'}>{'Title'}</span>, <span className={'description'}>{'Description'}</span> ])
-
This package provides a renderer that can be used to render components to pure JavaScript objects, without depending on the DOM or a native mobile environment. This package makes it easy to grab a snapshot of the platform view hierarchy (similar to a DOM tree) rendered by a ReactDOM or React Native without using a browser or
jsdom
.import TestRenderer from 'react-test-renderer' const Link = ({page, children}) => <a href={page}>{children}</a> const testRenderer = TestRenderer.create( <Link page={'https://www.facebook.com/'}>{'Facebook'}</Link> ) console.log(testRenderer.toJSON()) // { // type: 'a', // props: { href: 'https://www.facebook.com/' }, // children: [ 'Facebook' ] // }
-
ReactTestUtils are provided in the
with-addons
package and allow you to perform actions against a simulated DOM for the purpose of unit testing. -
Jest is a JavaScript unit testing framework created by Facebook based on Jasmine and provides automated mock creation and a
jsdom
environment. It's often used for testing components. -
There are couple of advantages compared to Jasmine:
- Automatically finds tests to execute in your source code.
- Automatically mocks dependencies when running your tests.
- Allows you to test asynchronous code synchronously.
- Runs your tests with a fake DOM implementation (via
jsdom
) so that your tests can be run on the command line. - Runs tests in parallel processes so that they finish sooner.
-
Let's write a test for a function that adds two numbers in
sum.js
file:const sum = (a, b) => a + b export default sum
Create a file named
sum.test.js
which contains actual test:import sum from './sum' test('adds 1 + 2 to equal 3', () => { expect(sum(1, 2)).toBe(3) })
And then add the following section to your
package.json
:{ "scripts": { "test": "jest" } }
Finally, run
yarn test
ornpm test
and Jest will print a result:$ yarn test PASS ./sum.test.js โ adds 1 + 2 to equal 3 (2ms)
-
Flux๋ ์ ํต์ ์ธ MVC ํจํด์ ๋์ฒดํ์ผ๋ก ์ฌ์ฉ๋๋ ์ ํ๋ฆฌ์ผ์ด์ ๋์์ธ ํจ๋ฌ๋ค์์ด๋ค. ์ด๊ฒ์ ๋จ์ํ ํ๋ ์์ํฌ๋ ๋ผ์ด๋ธ๋ฌ๋ฆฌ๊ฐ ์๋๋ผ React์ ๋จ๋ฐฉํฅ ๋ฐ์ดํฐ ํ๋ฆ์ ๊ฐ๋ ์ ๋ณด์ํ๋ ์๋ก์ด ์ข ๋ฅ์ ์ํคํ ์ฒ์ด๋ค. Facebook์ React๋ก ์์ ํ ๋ ์ด ํจํด์ ๋ด๋ถ์ ์ผ๋ก ์ฌ์ฉํ๋ค.
๋์คํจ์ฒ ๊ฐ ์ํฌํ๋ก์ฐ๋ ๋ค์๊ณผ ๊ฐ์ด ๊ณ ์ ํ ์ ๋ ฅ ๋ฐ ์ถ๋ ฅ์ผ๋ก ๊ตฌ์ฑ ์์๋ฅผ ์ ์ฅํ๊ณ ๋ณผ ์ ์๋ค.
-
Redux๋ Flux ๋์์ธ ํจํด์ ๊ธฐ๋ฐ์ผ๋กํ๋ JavaScript ์ฑ์ ์ํ ์์ธก ๊ฐ๋ฅํ ์ํ ์ปจํ ์ด๋์ด๋ค. Redux๋ React์ ํจ๊ป ๋๋ ๋ค๋ฅธ ๋ทฐ ๋ผ์ด๋ธ๋ฌ๋ฆฌ์ ํจ๊ป ์ฌ์ฉํ ์ ์๋ค. ํฌ๊ธฐ๊ฐ ์๊ณ (2kB ์ ๋) ์ข ์์ฑ์ด ์๋ค.
-
Redux๋ ์ธ ๊ฐ์ง ๊ธฐ๋ณธ์์น์ ๋ฐ๋ฅธ๋ค.
- ์ง์ค์ ๋จ์ผ ์์ค(Single source of truth): ์ ์ฒด ์ ํ๋ฆฌ์ผ์ด์ ์ state๋ ๋จ์ผ ์ ์ฅ์ ๋ด Object Tree์ ์ ์ฅ๋๋ค. ๋จ์ผ state ํธ๋ฆฌ๋ฅผ ์ฌ์ฉํ๋ฉด ์๊ฐ์ด ์ง๋จ์ ๋ฐ๋ฅธ ๋ณ๊ฒฝ์ฌํญ์ ์ถ์ ํ๊ณ ์์ฉ ํ๋ก๊ทธ๋จ์ ๋๋ฒ๊ทธ ๋๋ ๊ฒ์ฌํ๊ธฐ์ ์ฝ๋ค.
- State๋ ์ฝ๊ธฐ ์ ์ฉ: state๋ฅผ ๋ณ๊ฒฝํ๋ ์ ์ผํ ๋ฐฉ๋ฒ์ ๋ฌด์จ ์ผ์ด ์์๋์ง ์ค๋ช ํ ๊ฐ์ฒด์ธ action์ ๋ด๋ณด๋ด๋ ๊ฒ์ด๋ค. ๋ทฐ๋ ๋คํธ์ํฌ ์ฝ๋ฐฑ์ด state์ ์ง์ ์ฐ์ง ์๋๋ค.
- Changes are made with pure functions: ์์ ์ ์ํด state ํธ๋ฆฌ๊ฐ ๋ณํ๋๋ action์ ์ง์ ํ๋ ค๋ฉด reducer๋ฅผ ์์ฑํด์ผ ํ๋ค. Reducers๋ ์ด์ ์ state์ action์ ํ๋ผ๋ฏธํฐ๋ก ์ฌ์ฉํ๊ณ , ๋ค์์ state๋ฅผ return ํ๋ ์์ํจ์์ด๋ค.
-
๋จ์ ๋์ ์ Flux๋ฅผ ํตํด Redux์ ์ฌ์ฉํ๋ฉด ๋๋ฝํ๋ ์ผ์ด ๊ฑฐ์ ์๋ค.
- Mutation์ ํผํ๋ ๋ฐฉ๋ฒ์ ๋ฐฐ์์ผ ํ๋ค.: Flux๋ ๋ฐ์ดํฐ ๋ณ๊ฒฝ์ ๋ํด ์๊ฒฌ์ด ๋ง์ง ์์ง๋ง, Redux๋ mutation์ ์ข์ํ์ง ์์ผ๋ฉฐ Redux๋ฅผ ๋ณด์ํ๋ ๋ง์ ํจํค์ง๋ state๋ฅผ ์ ๋๋ก ๋ณ๊ฒฝํ์ง ์๋ ๊ฒ์ ๊ฐ์ ํ๋ค.
redux-immutable-state-invariant
, Immutable.js์ ๊ฐ์ ๊ฐ๋ฐ ์ ์ฉ ํจํค์ง๋ฅผ ์ฌ์ฉํ๊ฑฐ๋ ํ์๊ฒ ๋ณ๊ฒฝํ์ง ์๋ ์ฝ๋๋ฅผ ์์ฑํ๋๋ก ์ง์ํ์ฌ ์ ์ฉํ ์ ์๋ค. - packages๋ฅผ ์ ์คํ๊ฒ ์ ํํด์ผ ํ๋ค.: Flux๋ ์คํ ์ทจ์ / ๋ค์ ์คํ, ์ง์์ฑ ๋๋ ์์๊ณผ ๊ฐ์ ๋ฌธ์ ๋ฅผ ๋ช ์์ ์ผ๋ก ํด๊ฒฐํ๋ ค๊ณ ์๋ํ์ง ์์ง๋ง, Redux์๋ ๋ฏธ๋ค์จ์ด ๋ฐ ์ ์ฅ์ ํฅ์๊ธฐ์ ๊ฐ์ ์ต์คํ ์ ์ด ์์ผ๋ฉฐ ํ๋ถํ ์์ฝ ์์คํ ์ ์์ฑํ๋ค.
- ์์ง ์ข์ ํ๋ฆ ํตํฉ์ ์๋ค.: Flux๋ ํ์ฌ Redux๊ฐ ์ง์ํ์ง ์๋ ๋งค์ฐ ์ธ์์ ์ธ ์ ์ ์ ํ ๊ฒ์ฌ๋ฅผ ์ํ ํ ์ ์๋ค.
- Mutation์ ํผํ๋ ๋ฐฉ๋ฒ์ ๋ฐฐ์์ผ ํ๋ค.: Flux๋ ๋ฐ์ดํฐ ๋ณ๊ฒฝ์ ๋ํด ์๊ฒฌ์ด ๋ง์ง ์์ง๋ง, Redux๋ mutation์ ์ข์ํ์ง ์์ผ๋ฉฐ Redux๋ฅผ ๋ณด์ํ๋ ๋ง์ ํจํค์ง๋ state๋ฅผ ์ ๋๋ก ๋ณ๊ฒฝํ์ง ์๋ ๊ฒ์ ๊ฐ์ ํ๋ค.
-
mapStateToProps()
๋ ์ปดํฌ๋ํธ๊ฐ ๋ค๋ฅธ ์ปดํฌ๋ํธ์ ์ํด ์ ๋ฐ์ดํธ๋ state๋ฅผ ๋ฐ์์ค๋๋ฐ ๋์์ฃผ๋ ์ ํธ๋ฆฌํฐ์ด๋ค.const mapStateToProps = (state) => { return { todos: getVisibleTodos(state.todos, state.visibilityFilter) } }
mapDispatchToProps()
๋ ์ปดํฌ๋ํธ๊ฐ ์์ฉํ๋ก๊ทธ๋จ state ๋ณ๊ฒฝ์ ์ผ์ผํค๋ dispatching action ์ด๋ฒคํธ๋ฅผ ๋ฐ์์ํค๋๋ฐ ๋์์ฃผ๋ ์ ํธ๋ฆฌํฐ์ด๋ค.const mapDispatchToProps = (dispatch) => { return { onTodoClick: (id) => { dispatch(toggleTodo(id)) } } }
mapDispatchToProps
์ ๋ํด์ ํญ์ "๊ฐ์ฒด ์ฝ์(object shorthand)" ์์์ ์ฌ์ฉํ๋ ๊ฒ์ ์ถ์ฒํ๋ค.Redux๋ (โฆargs) => dispatch(onTodoClick(โฆargs))์ ๊ฐ์ ๋ค๋ฅธ ํจ์๋ก ๋ํํ๊ณ ํด๋น ๋ํผ ํจ์๋ฅผ โโ์ปดํฌ๋ํธ์ prop์ผ๋ก ์ ๋ฌํฉ๋๋ค.
const mapDispatchToProps = ({ onTodoClick })
-
reducer ๋ด์์ action์ ์ ๋ฌํ๋ ๊ฒ์ anti-pattern์ด๋ค. ๋ฆฌ๋์๋ ์ฌ์ด๋ ์ดํํธ(side effects) ๊ฐ ์์ด์ผ ํ๋ค. ๋จ์ํ action payload๋ฅผ ์ํํ๊ณ ์๋ก์ด state ๊ฐ์ฒด๋ฅผ ๋ฐํํ๋ค. reducer ๋ด์ ๋ฆฌ์ค๋๋ฅผ ์ถ๊ฐํ๊ณ action์ ์ ๋ฌํ๋ฉด, ์ฐ์์ ์ผ๋ก action ๋ฐ ๋ค๋ฅธ ์ฌ์ด๋ ์ดํํธ๊ฐ ๋ฐ์ํ ์ ์๋ค.
-
createStore()
๋ก ์์ฑ๋ ๋ชจ๋์์ store๋ฅผ ๋ด๋ณด๋ด๋ฉด ๋๋ค. ๋ํ, ์ ์ญ window ๊ฐ์ฒด๋ฅผ ์ค์ผ์ํค์ง ์์์ผ ํ๋ค.store = createStore(myReducer) export default store
-
- DOM ์กฐ์์ ๋น์ฉ์ด ๋ง์ด ๋ค์ด์ ์ ํ๋ฆฌ์ผ์ด์ ์ด ๋๋ฆฌ๊ณ ๋นํจ์จ์ ์ผ๋ก ์๋ํ๊ฒ ํ๋ค.
- ์ํ ์ข ์์ฑ์ผ๋ก ์ธํด, ๋ชจ๋ธ๊ณผ ๋ทฐ๋ฅผ ์ค์ฌ์ผ๋ก ํ ๋ณต์กํ ๋ชจ๋ธ์ด ์์ฑ๋๋ค.
- ๊ตฌ๊ธ ๊ฐ์ ๊ณต๋์์ ์์ฉํ๋ก๊ทธ๋จ์์๋ ๋ง์ ๋ฐ์ดํฐ ๋ณ๊ฒฝ์ด ๋ฐ์ํ๋ค.
- ์์ฒญ ๋ง์ ์ฝ๋๋ฅผ ์ถ๊ฐํ์ง ์๊ณ ์๋ ์ฝ๊ฒ ๋๋๋ฆด ๋ฐฉ๋ฒ์ด ์๋ค.
-
์ด ๋ผ์ด๋ธ๋ฌ๋ฆฌ๋ค์ ๋ค๋ฅธ ๋ชฉ์ ์ ๊ฐ์ง๋ค๋ ์ ์์ ๋งค์ฐ ๋ค๋ฅด์ง๋ง, ๋ชจํธํ ์ ์ฌ์ ์ ๊ฐ์ง๋ค.
Redux๋ ์ ํ๋ฆฌ์ผ์ด์ ์ ์ฒด์์ state๋ฅผ ๊ด๋ฆฌํ๊ธฐ ์ํ ๋๊ตฌ์ด๋ค. ์ผ๋ฐ์ ์ผ๋ก UI ์ํคํ ์ฒ๋ก ์ฌ์ฉ๋๋ค. ์ด๊ฒ์ Angular์ ์ ๋ฐ์ ํด๋นํ๋ ๋์์ด๋ผ๊ณ ์๊ฐํ์. RxJS๋ reactive ํ๋ก๊ทธ๋๋ฐ ๋ผ์ด๋ธ๋ฌ๋ฆฌ์ด๋ค. ์ผ๋ฐ์ ์ผ๋ก JavaScript์์ ๋น๋๊ธฐ ์์ ์ ์ํํ๋ ๋๊ตฌ๋ก ์ฌ์ฉ๋๋ค. ์ด๊ฒ์ Promises์ ๋์์ด๋ผ๊ณ ์๊ฐํ์. Redux๋ store๊ฐ reactive ํ๊ธฐ ๋๋ฌธ์ Reactive ํจ๋ฌ๋ค์์ ์ฌ์ฉํ๋ค. Store๋ ๋ฉ๋ฆฌ์ ํ๋์ ๊ด์ฐฐํ๊ณ ์ค์ค๋ก ๋ณํ์ํจ๋ค. RxJS๋ ๋ํ Reactive ํจ๋ฌ๋ค์์ ์ฌ์ฉํ์ง๋ง, ์ํคํ ์ฒ๋ฅผ ๋์ฒดํ๋ ๊ฒ์ด ์๋ ํจํด์ ๋ฌ์ฑํ๊ธฐ ์ํ ๊ธฐ๋ณธ ์ค๊ณ ๋ธ๋ก, Observables๋ฅผ ์ ๊ณตํ๋ค.
`componentDidMount()` ๋ฉ์๋์์ action์ ์ ๋ฌํ ์ ์๊ณ `render()` ๋ฉ์๋์์ ๋ฐ์ดํฐ๋ฅผ ํ์ธํ ์ ์๋ค.
```javascript
class App extends Component {
componentDidMount() {
this.props.fetchData()
}
render() {
return this.props.isLoaded
? <div>{'Loaded'}</div>
: <div>{'Not Loaded'}</div>
}
}
const mapStateToProps = (state) => ({
isLoaded: state.isLoaded
})
const mapDispatchToProps = { fetchData }
export default connect(mapStateToProps, mapDispatchToProps)(App)
```
-
container์์ store๋ฅผ ์ฌ์ฉํ๋ ค๋ฉด ๋ค์ ๋ ๋จ๊ณ๋ฅผ ์ํํด์ผ ํ๋ค.
-
mapStateToProps() ์ฌ์ฉํ๊ธฐ
: store์ state ๋ณ์๋ฅผ ์ง์ ํ props์ ๋งคํํ๋ค -
์์ props์ ์ปจํ ์ด๋๋ฅผ ์ฐ๊ฒฐํ๊ธฐ:
mapStateToProps
ํจ์๋ก ๋ฐํํ ๊ฐ์ฒด๊ฐ ์ปจํ ์ด๋์ ์ฐ๊ฒฐ๋์ด ์๋ค.react-redux
์์connect()
๋ฅผ ๊ฐ์ ธ์ฌ ์ ์๋ค.import React from 'react' import { connect } from 'react-redux' class App extends React.Component { render() { return <div>{this.props.containerData}</div> } } function mapStateToProps(state) { return { containerData: state.data } } export default connect(mapStateToProps)(App)
-
-
combineReducers()
์ผ๋ก ๋ง๋ค์ด์ง reducer์ action ์ฒ๋ฆฌ๋ฅผ ์์ํ๋ root reducer ๋ฅผ ์ ํ๋ฆฌ์ผ์ด์ ์ ์์ฑํด์ผ ํ๋ค.์๋ฅผ ๋ค์ด,
USER_LOGOUT
action ํ ์ด๊ธฐ state๋ฅผ ๋ฆฌํดํ๊ธฐ ์ํดrootReducer()
๋ฅผ ์ฌ์ฉํ์ฌ์ผ ํ๋ค. ์๋ค์ํผ, reducer๋ action๊ณผ ๊ด๊ณ์์ด ์ฒซ ๋ฒ์งธ ์ธ์๋กundefined
๋ก ํธ์ถ๋ ๋ ์ด๊ธฐ ์ํ๋ฅผ ๋ฐํํด์ผ ํ๋ค.const appReducer = combineReducers({ /* your app's top-level reducers */ }) const rootReducer = (state, action) => { if (action.type === 'USER_LOGOUT') { state = undefined } return appReducer(state, action) }
redux-persist
๋ฅผ ์ฌ์ฉํ๋ ๊ฒฝ์ฐ, storage๋ฅผ ์ ๋ฆฌํด์ผ ํ ์ ์๋ค.redux-persist
๋ state ์์ง์ storage ์์ง์ ๋ณด๊ดํ๋ค. ๋จผ์ , ์ ์ ํ storage ์์ง์ ๊ฐ์ ธ์์ state ํค๋ฅผ ์ ์ํ์ง ์๊ณ ์ ๋ฆฌํ๊ธฐ ์ ์ state๋ฅผ ๊ตฌ๋ฌธ ๋ถ์ํด์ผ ํ๋ค.const appReducer = combineReducers({ /* your app's top-level reducers */ }) const rootReducer = (state, action) => { if (action.type === 'USER_LOGOUT') { Object.keys(state).forEach(key => { storage.removeItem(`persist:${key}`) }) state = undefined } return appReducer(state, action) }
-
@ ๊ธฐํธ๋ ์ค์ ๋ก ๋ฐ์ฝ๋ ์ดํฐ๋ฅผ ๋ํ๋ด๋๋ฐ ์ฌ์ฉ๋๋ JavaScript ํํ์์ด๋ค. ๋ฐ์ฝ๋ ์ดํฐ ๋ฅผ ์ฌ์ฉํ๋ฉด ๋์์ธ ํ์์ ํด๋์ค์ ์์ฑ์ ์ฃผ์์ ๋ฌ๊ณ ์์ ํ ์ ์๋ค.
๋ฐ์ฝ๋ ์ดํฐ๋ฅผ ์ฌ์ฉํ๋ ๊ฒ๊ณผ ์ฌ์ฉํ์ง ์๊ณ Redux๋ฅผ ์ค์ ํ๋ ์๋ฅผ ์ดํด๋ณด์.
-
๋ฐ์ฝ๋ ์ดํฐ๊ฐ ์์ ๊ฒฝ์ฐ:
import React from 'react' import * as actionCreators from './actionCreators' import { bindActionCreators } from 'redux' import { connect } from 'react-redux' function mapStateToProps(state) { return { todos: state.todos } } function mapDispatchToProps(dispatch) { return { actions: bindActionCreators(actionCreators, dispatch) } } class MyApp extends React.Component { // ...define your main app here } export default connect(mapStateToProps, mapDispatchToProps)(MyApp)
-
๋ฐ์ฝ๋ ์ดํฐ๊ฐ ์์ ๊ฒฝ์ฐ:
import React from 'react' import * as actionCreators from './actionCreators' import { bindActionCreators } from 'redux' import { connect } from 'react-redux' function mapStateToProps(state) { return { todos: state.todos } } function mapDispatchToProps(dispatch) { return { actions: bindActionCreators(actionCreators, dispatch) } } @connect(mapStateToProps, mapDispatchToProps) export default class MyApp extends React.Component { // ...define your main app here }
์์ ์์ ๋ค๋ ๋ฐ์ฝ๋ ์ดํฐ ์ฌ์ฉ์ ์ ์ธํ๊ณ ๋ ๊ฑฐ์ ๋น์ทํ๋ค. ๋ฐ์ฝ๋ ์ดํฐ ๊ตฌ๋ฌธ์ ์์ง JavaScript ๋ฐํ์์ ๋ด์ฅ๋์ด ์์ง ์๋ค. ์คํ ์ค์ด๋ฉฐ ๋ณ๊ฒฝ๋ ์ ์๋ค. ํ์ฌ ๋ฐ์ฝ๋ ์ดํฐ ์ง์์ ์ํด์ babel์ ์ฌ์ฉํ๋ฉด ๋๋ค.
-
-
์์ฉ ํ๋ก๊ทธ๋จ์ Context ๋ฅผ ๋ฐ๋ก ์ฌ์ฉํ ์ ์์ผ๋ฉฐ ์ค๊ณ๋ ์ค์ฒฉ๋ ์ปดํฌ๋ํธ์ ๋ฐ์ดํฐ๋ฅผ ์ ๋ฌํ๋ ๋ฐ ์ ์ฉํ๋ค. ๋ฐ๋ฉด Redux ๋ ํจ์ฌ ๊ฐ๋ ฅํ๊ณ Context API๊ฐ ์ ๊ณตํ์ง ์๋ ๋ง์ ๊ธฐ๋ฅ์ ์ ๊ณตํ๋ค. ๋ํ, React Redux ๋ ๋ด๋ถ์ ์ผ๋ก Context๋ฅผ ์ฌ์ฉํ์ง๋ง, ์ด ์ฌ์ค์ public API์ ์๋ฆฌ์ง ์๋๋ค.
-
Reducers๋ ํญ์ state์ ๋์ ์ ๋ฐํํ๋ค(๋ชจ๋ ์ด์ ๊ณผ ํ์ฌ์ action์ ๊ธฐ๋ฐ์ผ๋ก ํ๋ค). ๊ทธ๋ฌ๋ฏ๋ก, state๋ฅผ ์ค์ด๋ ์ญํ ์ ํ๋ค. Redux reducer๊ฐ ํธ์ถ๋ ๋๋ง๋ค state ๋ฐ action์ด ๋งค๊ฐ๋ณ์๋ก ์ ๋ฌ๋๋ค. ๊ทธ๋ฐ ๋ค์ state๋ action์ ๋ฐ๋ผ ๊ฐ์(๋๋ ๋์ )๋๊ณ ๋ค์ state๊ฐ ๋ฐํ๋๋ค. action ์ปฌ๋ ์ ๋ฐ ๊ฒฐ๊ณผ์ ์ธ ์ต์ข state๋ฅผ ์ป๊ธฐ ์ํด ์ด๋ฌํ state๋ฅผ ์ํํ store์ ์ด๊ธฐ ์ํ๋ฅผ ์ค์ผ ์ ์๋ค.
-
๋น๋๊ธฐ action์ ์ ์ํ ์ ์๋
redux-thunk
๋ฏธ๋ค์จ์ด๋ฅผ ์ฌ์ฉํ ์ ์๋ค.fetch API ๋ฅผ ์ฌ์ฉํ์ฌ ํน์ ๊ณ์ ์ AJAX ํธ์ถ๋ก ๊ฐ์ ธ์ค๋ ์์๋ฅผ ๋ค์ด๋ณด์.
export function fetchAccount(id) { return dispatch => { dispatch(setLoadingAccountState()) // Show a loading spinner fetch(`/account/${id}`, (response) => { dispatch(doneFetchingAccount()) // Hide loading spinner if (response.status === 200) { dispatch(setAccount(response.json)) // Use a normal function to set the received state } else { dispatch(someError) } }) } } function setAccount(data) { return { type: 'SET_Account', data: data } }
-
Redux store์ ๋ฐ์ดํฐ๋ฅผ ์ ์งํ๊ณ , ์ปดํฌ๋ํธ ๋ด๋ถ์ UI ๊ด๋ จ state๋ฅผ ์ ์งํ๋ฉด ๋๋ค.
-
์ปดํฌ๋ํธ์์ store์ ์ ๊ทผํ๋ ๊ฐ์ฅ ์ข์ ๋ฐฉ๋ฒ์
connect()
ํจ์๋ฅผ ์ฌ์ฉํ์ฌ ๊ธฐ์กด ์ปดํฌ๋ํธ๋ฅผ ๊ฐ์ธ๋ ์๋ก์ด ์ปดํฌ๋ํธ๋ฅผ ๋ง๋๋ ๊ฒ์ด๋ค. ์ด๋ฌํ ํจํด์ Higher-Order Components ๋ผ๊ณ ํ๋ฉฐ, ์ผ๋ฐ์ ์ผ๋ก React์์ ์ปดํฌ๋ํธ์ ๊ธฐ๋ฅ์ ํ์ฅํ๋๋ฐ ์ ํธ๋๋ ๋ฐฉ๋ฒ์ด๋ค. ์ด๋ฅผ ํตํด state ๋ฐ action ์์ฑ์๋ฅผ ์ปดํฌ๋ํธ์ ๋งคํํ๊ณ store ์ ๋ฐ์ดํธ ์ ์๋์ผ๋ก ์ ๋ฌํ ์ ์๋ค.connect๋ฅผ ์ฌ์ฉํ
<FilterLink>
์ปดํฌ๋ํธ ์์๋ฅผ ๋ณด์.import { connect } from 'react-redux' import { setVisibilityFilter } from '../actions' import Link from '../components/Link' const mapStateToProps = (state, ownProps) => ({ active: ownProps.filter === state.visibilityFilter }) const mapDispatchToProps = (dispatch, ownProps) => ({ onClick: () => dispatch(setVisibilityFilter(ownProps.filter)) }) const FilterLink = connect( mapStateToProps, mapDispatchToProps )(Link) export default FilterLink
์ฑ๋ฅ ์ต์ ํ๊ฐ ์๋นํ ์ ๊ณ ์ผ๋ฐ์ ์ผ๋ก ๋ฒ๊ทธ๊ฐ ๋ฐ์ํ ๊ฐ๋ฅ์ฑ์ด ์๊ธฐ ๋๋ฌธ์ Redux ๊ฐ๋ฐ์๋ ํญ์ Context API๋ฅผ ์ฌ์ฉํ์ฌ ์ ์ฅ์์ ์ ๊ทผํ๋ ๊ฒ๋ณด๋ค๋
connect()
๋ฅผ ์ฌ์ฉํ๋ ๊ฒ์ด ์ข๋ค.class MyComponent { someMethod() { doSomethingWith(this.context.store) } }
-
Component ๋ ์ ํ๋ฆฌ์ผ์ด์ ์ presentational ๋ถ๋ถ์ ๋ฌ์ฌํ๋ ํด๋์ค ๋๋ ํจ์ํ ์ปดํฌ๋ํธ์ด๋ค.
Container ๋ Redux store์ ์ฐ๊ฒฐ๋ ์ปดํฌ๋ํธ์ ๋ํ ๋น๊ณต์ ์ฉ์ด์ด๋ค. ์ปจํ ์ด๋๋ Redux state ์ ๋ฐ์ดํธ ๋ฐ dispatch action์ ๊ตฌ๋ ํ๋ฉฐ ์ผ๋ฐ์ ์ผ๋ก DOM ์์๋ฅผ ๋ ๋๋งํ์ง ์๋๋ค. ๊ทธ๋ค์ presentational ์์ ์ปดํฌ๋ํธ์ ๋ ๋๋ง์ ์์ํ๋ค.
-
์์๋ฅผ ์ฌ์ฉํ๋ฉด IDE๋ฅผ ์ฌ์ฉํ ๋ ํ๋ก์ ํธ ์ ์ฒด์์ ํน์ ๊ธฐ๋ฅ์ ๋ชจ๋ ์ฌ์ฉ๋ฒ์ ์ฝ๊ฒ ์ฐพ์ ์ ์๋ค. ์คํ๊ฐ์ ๊ฒฝ์ฐ
ReferenceError
๋ฅผ ๋์ ธ์ฃผ์ด ๋ฒ๊ทธ ๋ฐ์์ ์ฌ์ ์ ๋ฐฉ์งํ๋ค.๋ณดํต ํ๋์ ํ์ผ๋ก ์ ์ฅํ๋ค (
constants.js
๋๋actionTypes.js
).export const ADD_TODO = 'ADD_TODO' export const DELETE_TODO = 'DELETE_TODO' export const EDIT_TODO = 'EDIT_TODO' export const COMPLETE_TODO = 'COMPLETE_TODO' export const COMPLETE_ALL = 'COMPLETE_ALL' export const CLEAR_COMPLETED = 'CLEAR_COMPLETED'
Redux์์๋ ๋ ๊ณณ์์ ์ฌ์ฉํ๋ค.
-
action์ ์์ฑํ ๋
actions.js
์ ๋ณด์.import { ADD_TODO } from './actionTypes'; export function addTodo(text) { return { type: ADD_TODO, text } }
-
reducer ์์์
reducer.js
๋ฅผ ๋ง๋ค์ด ๋ณด์.import { ADD_TODO } from './actionTypes' export default (state = [], action) => { switch (action.type) { case ADD_TODO: return [ ...state, { text: action.text, completed: false } ]; default: return state } }
-
`mapDispatchToProps()`์์ *action creators*๋ฅผ `dispatch()`์ ๋ฐ์ธ๋ฉํ๋ ๋ช ๊ฐ์ง ๋ฐฉ๋ฒ์ด ์๋ค. ๊ฐ๋ฅํ ์ต์
์ ์๋์ ๊ฐ๋ค.
```javascript
const mapDispatchToProps = (dispatch) => ({
action: () => dispatch(action())
})
```
```javascript
const mapDispatchToProps = (dispatch) => ({
action: bindActionCreators(action, dispatch)
})
```
```javascript
const mapDispatchToProps = { action }
```
์ธ ๋ฒ์งธ ์ต์
์ ์ฒซ ๋ฒ์งธ ์ต์
์ ์ฝ์ด์ด๋ค.
-
ownProps
๋งค๊ฐ ๋ณ์๊ฐ ์ง์ ๋๋ฉด, React Redux๋ ์ปดํฌ๋ํธ์ ์ ๋ฌ๋ props๋ฅผ connect ํจ์๋ก ์ ๋ฌํ๋ค. ๋ฐ๋ผ์, ์ฐ๊ฒฐ๋ ์ปดํฌ๋ํธ๋ฅผ ์ฌ์ฉํ๋ ๊ฒฝ์ฐ,import ConnectedComponent from './containers/ConnectedComponent'; <ConnectedComponent user={'john'} />
mapStateToProps()
๊ณผmapDispatchToProps()
ํจ์ ๋ด์์ownProps
๋ ๊ฐ์ฒด๊ฐ ๋๋ค.{ user: 'john' }
์ด ๊ฐ์ฒด๋ฅผ ์ฌ์ฉํ์ฌ ํด๋น ํจ์์์ ๋ฌด์์ ๋ฐํํ ์ง ๊ฒฐ์ ํ ์ ์๋ค.
-
๋๋ถ๋ถ์ ์์ฉ ํ๋ก๊ทธ๋จ์๋ ์๋์ ๊ฐ์ ์ฌ๋ฌ ์ต์์ ๋๋ ํฐ๋ฆฌ๊ฐ ์๋ค.
- Components: Redux๋ฅผ ์ธ์ํ์ง ๋ชปํ๋ dumb ์ปดํฌ๋ํธ์ ์ฌ์ฉ๋๋ค.
- Containers: Redux์ ์ฐ๊ฒฐ๋ smart ์ปดํฌ๋ํธ์ ์ฌ์ฉ๋๋ค.
- Actions: ๋ชจ๋ action creator์ ์ฌ์ฉ๋๋ฉฐ, ํ์ผ ์ด๋ฆ์ด ์ฑ์ ์ผ๋ถ์ด๋ค.
- Reducers: ๋ชจ๋ reducer์ ์ฌ์ฉ๋๋ฉฐ, ํ์ผ ์ด๋ฆ์ state ํค์ด๋ค.
- Store: store ์ด๊ธฐํ์ ์ฌ์ฉ๋๋ค.
์ด ๊ตฌ์กฐ๋ ์ค์๊ท๋ชจ์ ์ฑ์ ์ ํฉํ๋ค.
-
redux-saga
๋ React/Redux ์ ํ๋ฆฌ์ผ์ด์ ์์ side-effect(๋ฐ์ดํฐ ๊ฐ์ ธ์ค๊ธฐ ๊ฐ์ ๋น๋๊ธฐ์ ๋ฐ ๋ธ๋ผ์ฐ์ ์บ์ ์ ๊ทผ๊ณผ ๊ฐ์ด ๋ถ์พํ ๊ฒ)๋ฅผ ๋์ฑ ์ฝ๊ณ ํจ๊ณผ์ ์ผ๋ก ์ฒ๋ฆฌํ๋ ๊ฒ์ ๋ชฉํ๋ก ํ๋ ๋ผ์ด๋ธ๋ฌ๋ฆฌ์ด๋ค.NPM์์ ์ฌ์ฉ ๊ฐ๋ฅํ๋ค.
$ npm install --save redux-saga
-
Saga ๋ ์์ฉ ํ๋ก๊ทธ๋จ์์ ๋ณ๋์ thread์ ์ ์ฌํ๋ฉฐ, side-effect ์ ์ฃผ๋ ์์ธ์ด๋ค.
redux-saga
๋ redux middleware๋ก, ์ด thread๋ ์ผ๋ฐ์ ์ธ Redux action์ผ๋ก ๋ฉ์ธ ์์ฉ ํ๋ก๊ทธ๋จ์์ ์์, ์ผ์ ์ค์ง ๋ฐ ์ทจ์ ํ ์ ์์ผ๋ฉฐ, ์ ์ฒด Redux ์์ฉ ํ๋ก๊ทธ๋จ state์ ์ ๊ทผํ ์ ์์ผ๋ฉฐ, Redux action๋ ์ ๋ฌํ ์ ์๋ค. -
call()
๊ณผput()
๋ ๋ค effect ์์ฑํจ์์ด๋ค.call()
ํจ์๋ effect ์ค๋ช ์ ์์ฑํ๋ ๋ฐ ์ฌ์ฉ๋๋ฉฐ ๋ฏธ๋ค์จ์ด๊ฐ promise๋ฅผ ํธ์ถํ๋๋ก ํ๋ค.put()
ํจ์๋ effect๋ฅผ ์์ฑํ์ฌ ๋ฏธ๋ค์จ์ด๊ฐ action์ store์ ์ ๋ฌํ๋๋ก ํด์ค๋ค.ํน์ ์ฌ์ฉ์ ๋ฐ์ดํฐ๋ฅผ ๊ฐ์ ธ์ค๋๋ฐ effect๊ฐ ์ด๋ป๊ฒ ์๋ํ๋์ง ์๋ฅผ ๋ค์ด๋ณด์.
function* fetchUserSaga(action) { // `call` function accepts rest arguments, which will be passed to `api.fetchUser` function. // Instructing middleware to call promise, it resolved value will be assigned to `userData` variable const userData = yield call(api.fetchUser, action.userId) // Instructing middleware to dispatch corresponding action. yield put({ type: 'FETCH_USER_SUCCESS', userData }) }
-
Redux Thunk ๋ฏธ๋ค์จ์ด๋ action ๋์ ํจ์๋ฅผ ๋ฆฌํดํ๋ action ์์ฑ์๋ฅผ ์์ฑํ๋๋ก ํด์ค๋ค. thunk๋ action์ dispatch๋ฅผ ์ง์ฐ์ํค๊ฑฐ๋ ํน์ ํ ์กฐ๊ฑด์ ๋ง์กฑํ ๊ฒฝ์ฐ dispatch ํด์ผ ํ๋ ๊ณณ์ ์ฌ์ฉ๋๋ค. ๋ด๋ถ ํจ์๋ store ๋ฉ์๋
dispatch()
์getState()
๋ฅผ ๋งค๊ฐ๋ณ์๋ก ๋ฐ๋๋ค. -
Redux Thunk์ Redux Saga๋ side effects๋ฅผ ์ฒ๋ฆฌํ๋ค. ๋๋ถ๋ถ์ ์๋๋ฆฌ์ค์์ Thunk๋ Promises๋ฅผ ์ฌ์ฉํ์ฌ ์ฒ๋ฆฌํ๊ณ Saga๋ Generators๋ฅผ ์ฌ์ฉํ๋ค. Thunk๋ ์ฌ์ฉํ๊ธฐ ์ฝ๊ณ Promises๋ ๋ง์ ๊ฐ๋ฐ์์๊ฒ ์น์ํ๋ค. Sagas/Generators๋ ๋ ๊ฐ๋ ฅํ์ง๋ง, ํ์ต์ ํ ํ์๊ฐ ์๋ค. ๊ทธ๋ฌ๋ ๋ ๋ฏธ๋ค์จ์ด๋ ๊ณต์กดํ ์ ์์ด์ ํ์ํ ๋ Thunks๋ก ์์ํ๊ณ ํ์ํ ๋ Sagas๋ฅผ ์๊ฐํ ์ ์๋ค.
-
Redux DevTools๋ ํซ ๋ฆฌ๋ก๋ฉ, action ๋ฆฌํ๋ ์ด ๋ฐ ์ฌ์ฉ์ ์ ์ UI ๊ฐ๋ฅํ UI๊ฐ ์๋ Redux์ ์ค์๊ฐ ํธ์งํ๋ ์๊ฐ ์ฌํ ํ๊ฒฝ์ด๋ค. Redux DevTools ์ค์น ์ ํ๋ก์ ํธ ํตํฉ์ ๋ฐฉํดํ์ง ์์ผ๋ ค๋ฉด, Chrome ๋ฐ Firefox ์ฉ Redux DevTools Extension ์ฌ์ฉ์ ๊ณ ๋ คํด์ผ ํ๋ค.
-
- ๋ชจ๋ state์ action payload๋ฅผ ๊ฒ์ฌํ ์ ์๋ค.
- action์ ์ทจ์ํ์ฌ ์๊ฐ์ ๋๋๋ฆด ์ ์๋ค.
- reducer ์ฝ๋๋ฅผ ๋ณ๊ฒฝํ๋ฉด, ๊ฐ ๋จ๊ณ๋ณ action์ด ๋ค์ ํ๊ฐ๋๋ค.
- reducers๊ฐ throw๋๋ฉด, ์ด๋ค action์ด ๋ฐ์ํ๋์ง์ ์ค๋ฅ๊ฐ ๋ฌด์์ธ์ง ์ ์ ์๋ค.
persistState()
store enhancer๋ฅผ ์ฌ์ฉํ๊ฒ ๋๋ฉด, ํ์ด์ง๋ฅผ ๋ค์ ๋ก๋ํด๋ ๋๋ฒ๊ทธ ์ธ์ ์ ์ ์งํ ์ ์๋ค.
-
Selectors๋ Redux state๋ฅผ ์ธ์๋ก ์ฌ์ฉํ๋ฉฐ ์ผ๋ถ ๋ฐ์ดํฐ๋ฅผ component์ ์ ๋ฌํ๋ ํจ์์ด๋ค.
์๋ฅผ ๋ค์ด, state์์ ์ฌ์ฉ์ ์ธ๋ถ ์ฌํญ์ ์ป์ผ๋ ค๋ฉด ์๋์ ๊ฐ์ด ํ๋ฉด ๋๋ค.
const getUserData = state => state.user.data
-
Redux Form์ React ๋ฐ Redux์ ํจ๊ป ์๋ํ๋ฉฐ React์ ์์์์ Redux๋ฅผ ์ฌ์ฉํ์ฌ ๋ชจ๋ state๋ฅผ ์ ์ฅํ ์ ์๋ค. Redux Form์ ์์ HTML5 ์ ๋ ฅ๊ณผ ํจ๊ป ์ฌ์ฉํ ์ ์์ผ๋ฉฐ, Material UI, React Widgets ๋ฐ React Bootstrap๊ณผ ๊ฐ์ ์ผ๋ฐ์ ์ธ UI ํ๋ ์์ํฌ์๋ ์ ์๋ํ๋ค.
-
- Redux store๋ฅผ ํตํ ํ๋ ๊ฐ ์ง์.
- ์ ํจ์ฑ ๊ฒ์ฌ (sync/async) ๋ฐ ์ ์ถ.
- ํ๋ ๊ฐ์ ํ์ํ, parsing ๋ฐ ํ์คํ.
-
applyMiddleware()
๋ฅผ ์ฌ์ฉํ ์ ์๋ค.์๋ฅผ ๋ค์ด,
redux-thunk
์logger
๋ฅผ ์ถ๊ฐํ์ฌapplyMiddleware()
์ ์ธ์๋ก ์ ๋ฌํ ์ ์๋ค.import { createStore, applyMiddleware } from 'redux' const createStoreWithMiddleware = applyMiddleware(ReduxThunk, logger)(createStore)
-
createStore์ ๋๋ฒ์งธ ์ธ์๋ก ์ด๊ธฐ state๋ฅผ ์ ๋ฌํด์ผํ๋ค.
const rootReducer = combineReducers({ todos: todos, visibilityFilter: visibilityFilter }) const initialState = { todos: [{ id: 123, name: 'example', completed: false }] } const store = createStore( rootReducer, initialState )
-
Relay์ Redux๋ ๋ ๋ค ๋จ์ผ ์ ์ฅ์๋ฅผ ์ฌ์ฉํ๋ค๋ ์ ์์ ์ ์ฌํ๋ค. ๊ฐ์ฅ ํฐ ์ฐจ์ด์ ์ relay๋ ์๋ฒ์์ ์์๋ state๋ง ๊ด๋ฆฌํ๊ณ ์ํ์ ๋ํ ๋ชจ๋ ์ ๊ทผ์ GraphQL queries(๋ฐ์ดํฐ ์ฝ๊ธฐ) ๋ฐ mutations(๋ฐ์ดํฐ ๋ณ๊ฒฝ)๋ฅผ ํตํด์ ํ๋ค. Relay๋ ๋ฐ์ดํฐ๋ฅผ ์บ์ํ๊ณ ๋ณ๊ฒฝ๋ ๋ฐ์ดํฐ๋ง ๊ฐ์ ธ์ค๊ณ ๋ ์ด์ ๊ฐ์ ธ์ค์ง ์๊ธฐ ๋๋ฌธ์ ๋ฐ์ดํฐ ๊ฐ์ ธ์ค๊ธฐ๋ฅผ ์ต์ ํํ ์ ์๋ค.
-
React is a JavaScript library, supporting both front end web and being run on the server, for building user interfaces and web applications.
React Native is a mobile framework that compiles to native app components, allowing you to build native mobile applications (iOS, Android, and Windows) in JavaScript that allows you to use React to build your components, and implements React under the hood.
-
React Native can be tested only in mobile simulators like iOS and Android. You can run the app in your mobile using expo app (https://expo.io) Where it syncs using QR code, your mobile and computer should be in same wireless network.
-
You can use
console.log
,console.warn
, etc. As of React Native v0.29 you can simply run the following to see logs in the console:$ react-native log-ios $ react-native log-android
-
Follow the below steps to debug React Native app:
- Run your application in the iOS simulator.
- Press
Command + D
and a webpage should open up athttp://localhost:8081/debugger-ui
. - Enable Pause On Caught Exceptions for a better debugging experience.
- Press
Command + Option + I
to open the Chrome Developer tools, or open it viaView
->Developer
->Developer Tools
. - You should now be able to debug as you normally would.
-
Reselect๋ ๋ฉ๋ชจ์ด์ ์ด์ ๊ฐ๋ ์ ์ฌ์ฉํ๋ selector library (Redux ์ฉ)์ด๋ค. ์๋ Redux์ ๊ฐ์ ์ ํ๋ฆฌ์ผ์ด์ state์์ ํ์๋ ๋ฐ์ดํฐ๋ฅผ ๊ณ์ฐํ๊ธฐ ์ํด ์์ฑ๋์์ง๋ง, ์ํคํ ์ณ๋ ๋ผ์ด๋ธ๋ฌ๋ฆฌ์๋ ์ฐ๊ฒฐํ ์ ์๋ค.
Reselect์ ๋ง์ง๋ง ํธ์ถ์ ๋ง์ง๋ง ์ /์ถ๋ ฅ ์ฌ๋ณธ์ ์ ์งํ๊ณ , ์ ๋ ฅ ์ค ํ๋๊ฐ ๋ณ๊ฒฝ๋ ๊ฒฝ์ฐ์๋ง ๊ฒฐ๊ณผ๋ฅผ ๋ค์ ๊ณ์ฐํ๋ค. ๋์ผํ ์ ๋ ฅ์ด ๋์์ ๋ ๋ฒ ์ ๊ณต๋๋ฉด, Reselect๋ ์บ์๋ ์ถ๋ ฅ์ ๋ฐํํ๋ค. ๋ฉ๋ชจ ๋ฐ ์บ์๋ ์์ ํ ์ฌ์ฉ์ ์ ์ํ ์ ์๋ค.
-
Flow๋ JavaScript์์ ํ์ ์ค๋ฅ๋ฅผ ์ก์๋ด๊ธฐ ์ํด ์ค๊ณ๋ ์ ์ ํ์ ๊ฒ์ฌ๊ธฐ ์ด๋ค. Flow ํ์ ์ ๊ธฐ์กด ํ์ ์์คํ ๋ณด๋ค ํจ์ฌ ์ธ๋ฐํ ์ฐจ์ด๋ฅผ ํํํ ์ ์๋ค. ์๋ฅผ ๋ค์ด, Flow๋ ๋๋ถ๋ถ์ ํ์ ์์คํ ๊ณผ ๋ฌ๋ฆฌ
null
๊ณผ ๊ด๋ จ๋ ์ค๋ฅ๋ฅผ ์ฐพ์๋ด๋ ๋ฐ ๋์์ด ๋๋ค. -
Flow๋ ์ธ์ด์ ์์ ์งํฉ์ ์ฌ์ฉํ๋ ์ ์ ๋ถ์ ๋๊ตฌ (์ ์ ๊ฒ์ฌ๊ธฐ)๋ก, ๋ชจ๋ ์ฝ๋์ ํ์ ์ด๋ ธํ ์ด์ ์ ์ถ๊ฐํ์ฌ ์ปดํ์ผ ํ์์ ์ ์ฒด ๋ฒ๊ทธ ํด๋์ค๋ฅผ ์ก์ ์ ์๋ค. PropTypes์ React์ ํจ์น๋ ๊ธฐ๋ณธ ํ์ ๊ฒ์ฌ๊ธฐ (๋ฐํ์ ๊ฒ์ฌ๊ธฐ)์ด๋ค. ์ฃผ์ด์ง ์ปดํฌ๋ํธ์ ์ ๋ฌ๋๋ props ํ์ ์ด์ธ์ ๊ฒ์ ํ์ธํ ์ ์๋ค. ์ ์ฒด ํ๋ก์ ํธ์ ์ ์ฐํ ํ์ ๊ฒ์ฌ๋ฅผ ์ํ ๊ฒฝ์ฐ Flow / TypeScript๊ฐ ์ ํฉํ๋ค.
-
์๋์ ์์ ๋ React์ Font Awesome์ ํฌํจ์ํค๋ ๊ฒ์ด๋ค.
font-awesome
์ค์นํ๋ค.
$ npm install --save font-awesome
index.js
ํ์ผ์font-awesome
Importํ๋ค.
import 'font-awesome/css/font-awesome.min.css'
className
์ Font Awesome ํด๋์ค๋ฅผ ์ถ๊ฐํ๋ค.
render() { return <div><i className={'fa fa-spinner'} /></div> }
-
React Developer Tools ๋ฅผ ์ฌ์ฉํ๋ฉด ์ปดํฌ๋ํธ props์ state๋ฅผ ํฌํจํ ์ปดํฌ๋ํธ ๊ณ์ธต์ ๊ฒ์ฌํ ์ ์๋ค. ๋ธ๋ผ์ฐ์ ํ์ฅ (Chrome๊ณผ Firefox ์ฉ), ๋ ๋ฆฝ์คํํ ์ฑ(Safari, IE, ์ React Native ๋ฑ์ ๋ค๋ฅธ ํ๊ฒฝ์์ ์๋ํ๋)์ผ๋ก ์๋ค.
๋ค๋ฅธ ๋ธ๋ผ์ฐ์ ๋๋ ํ๊ฒฝ์์ ์ฌ์ฉ ๊ฐ๋ฅํ ๊ณต์ ํ์ฅ
- Chrome extension
- Firefox extension
- Standalone app (Safari, React Native, etc)
-
๋ธ๋ผ์ฐ์ ์์ ๋ก์ปฌ HTML ํ์ผ(
file://...
)์ ์ฐ ๊ฒฝ์ฐ ๋จผ์ Chrome Extensions์ ์ด๊ณAllow access to file URLs
์ ์ ํํด์ผ ํ๋ค. -
-
Polymer ์๋ฆฌ๋จผํธ๋ฅผ ๋ง๋ ๋ค.
<link rel='import' href='../../bower_components/polymer/polymer.html' /> Polymer({ is: 'calender-element', ready: function() { this.textContent = 'I am a calender' } })
-
Polymer ์ปดํฌ๋ํธ HTML ํ๊ทธ๋ฅผ HTML ๋ฌธ์๋ก ๊ฐ์ ธ์์ ๋ง๋ ๋ค. (์ : React ์ ํ๋ฆฌ์ผ์ด์ ์
index.html
๋ก ๊ฐ์ ธ์ค์ญ์์ค.)<link rel='import' href='./src/polymer-components/calender-element.html'>
- JSX ํ์ผ์์ ํด๋น ์๋ฆฌ๋จผํธ๋ฅผ ์ฌ์ฉํ๋ค.
import React from 'react' class MyComponent extends React.Component { render() { return ( <calender-element /> ) } } export default MyComponent
-
-
React๊ฐ Vue.js์ ๋นํด ์๋์ ๊ฐ์ ์ฅ์ ์ด ์๋ค.
- ๋๊ท๋ชจ ์ฑ ๊ฐ๋ฐ์ ์์ด์ ๋ ๋ง์ ์ ์ฐ์ฑ์ ์ ๊ณตํ๋ค.
- ํ ์คํธํ๊ธฐ ์ฝ๋ค.
- ๋ชจ๋ฐ์ผ ์ฑ ์ ์์ ์ ํฉํ๋ค.
- ๋ ๋ง์ ์ ๋ณด์ ์ ์ฉํ ์๋ฃจ์ ์ ๊ฐ์ง๊ณ ์๋ค.
-
React Angular React๋ ๋ผ์ด๋ธ๋ฌ๋ฆฌ์ด๋ฉฐ View ๋จ๋ง ์๋ค. Angular๋ ํ๋ ์์ํฌ์ด๋ฉฐ ์์ ํ MVC ๊ธฐ๋ฅ์ด ์๋ค. React๋ ์๋ฒ ์ธก์์ ๋ ๋๋งํ ์ ์๋ค. AngularJS๋ ํด๋ผ์ด์ธํธ ์ธก์์๋ง ๋ ๋๋ง ๋์ง๋ง Angular 2 ์ด์์์ ์๋ฒ ์ธก์์ ๋ ๋๋ง์ด ๋๋ค. React๋ ํผ๋์ค๋ฌ์ธ ์ ์๋ JS๋ฅผ HTML์ฒ๋ผ ๋ณด์ด๋ JSX๋ฅผ ์ฌ์ฉํ๋ค. Angular๋ HTML์ ๋ํ ํ ํ๋ฆฟ ์ ๊ทผ ๋ฐฉ์์ ๋ฐ๋ฅด๋ฏ๋ก ์ฝ๋๊ฐ ๋ ์งง๊ณ ์ดํดํ๊ธฐ ์ฝ๋ค. ๋ชจ๋ฐ์ผ ์ ํ๋ฆฌ์ผ์ด์ ์ ๋น๋ํ๊ธฐ ์ํ React ์ ํ์ธ React Native๋ ๋ ๋น ๋ฅด๊ณ ์์ ์ ์ด๋ค. Ionic, Angular์ ๋ชจ๋ฐ์ผ ๋ค์ดํฐ๋ธ ์ฑ์ ์๋์ ์ผ๋ก ๋ ์์ ์ ์ด๋ฉฐ ๋๋ฆฌ๋ค. React์์ ๋ฐ์ดํฐ๋ ํ ๋ฐฉํฅ์ผ๋ก๋ง ํ๋ฅด๋ฏ๋ก ๋๋ฒ๊น ์ด ์ฝ๋ค. Angular์์๋ ๋ฐ์ดํฐ๊ฐ ์๋ฐฉํฅ์ผ๋ก ํ๋ฅธ๋ค. ์ฆ ์์๊ณผ ๋ถ๋ชจ ์ฌ์ด์ ์๋ฐฉํฅ ๋ฐ์ดํฐ ๋ฐ์ธ๋ฉ์ด ์์์ผ๋ก ๋๋ฒ๊น ์ด ์ด๋ ค์ด ๊ฒฝ์ฐ๊ฐ ๋ง๋ค. -
ํ์ด์ง๊ฐ ๋ก๋ฉ๋๋ฉด, React DevTools๊ฐ
__REACT_DEVTOOLS_GLOBAL_HOOK__
์ด๋ผ๋ ์ ์ญ์ ์ค์ ํ ํ ์ด๊ธฐํ ์ค์ React๊ฐ ํด๋น hook๊ณผ ํต์ ํ๋ค. ์น์ฌ์ดํธ๊ฐ React๋ฅผ ์ฌ์ฉํ์ง ์๊ฑฐ๋ React๊ฐ DevTool๊ณผ ํต์ ํ์ง ๋ชปํ๋ฉด ํญ์ด ํ์๋์ง ์๋๋ค. -
styled-components
๋ React ์ ํ๋ฆฌ์ผ์ด์ ์คํ์ผ๋ง์ ์ํ JavaScript ๋ผ์ด๋ธ๋ฌ๋ฆฌ์ด๋ค. ์คํ์ผ๊ณผ ์ปดํฌ๋ํธ ๊ฐ์ ๋งคํ์ ์ ๊ฑฐํ๊ณ JavaScript๋ก ๋ณด๊ฐ๋ ์ค์ CSS๋ฅผ ์์ฑํ ์ ์๋ค. -
๊ฐ๊ฐ์ ๋ํด ํน์ ์คํ์ผ๋ก
<Title>
๊ณผ<Wrapper>
์ปดํฌ๋ํธ๋ฅผ ๋ง๋ค์ด ๋ณด์.import React from 'react' import styled from 'styled-components' // Create a <Title> component that renders an <h1> which is centered, red and sized at 1.5em const Title = styled.h1` font-size: 1.5em; text-align: center; color: palevioletred; ` // Create a <Wrapper> component that renders a <section> with some padding and a papayawhip background const Wrapper = styled.section` padding: 4em; background: papayawhip; `
Title
๊ณผWrapper
๋ ์ด์ ์๋ก ๋ค๋ฅธ react component์ฒ๋ผ ๋ ๋๋ง ํ ์ ์๋ ์ปดํฌ๋ํธ์ด๋ค.<Wrapper> <Title>{'Lets start first styled component!'}</Title> </Wrapper>
-
Relay๋ React View ๊ณ์ธต์ ์ฌ์ฉํ์ฌ ์น ์ ํ๋ฆฌ์ผ์ด์ ์ ๋ฐ์ดํฐ ๊ณ์ธต๊ณผ ํด๋ผ์ด์ธํธ-์๋ฒ ํต์ ์ ์ ๊ณตํ๊ธฐ ์ํ JavaScript ํ๋ ์์ํฌ์ด๋ค.
-
react-scripts@2.1.0 ์ด์๋ถํฐ, typescript๋ฅผ ๊ธฐ๋ณธ์ ์ผ๋ก ์ง์ํ๋ค. ์๋์ ๊ฐ์ด
--typescript
์ต์ ์ ์ ๋ฌํ ์ ์๋ค.npx create-react-app my-app --typescript # or yarn create react-app my-app --typescript
๊ทธ๋ฌ๋ ๋ ๋ฎ์ ๋ฒ์ ์ react scripts์ ๊ฒฝ์ฐ ์ ํ๋ก์ ํธ๋ฅผ ๋ง๋๋ ๋์
--scripts-version
์ต์ ์react-scripts-ts
๋ก ์ ๊ณตํ์ฌ๋ผ.react-scripts-ts
๋ ํ์คcreate-react-app
ํ๋ก์ ํธ ํ์ดํ๋ผ์ธ์ ์ทจํ๊ณ TypeScript๋ฅผ ๋ฏน์ค๋ก ๊ฐ์ ธ์ค๊ธฐ ์ํ ์กฐ์ ์ธํธ์ด๋ค.์ด์ ํ๋ก์ ํธ ๋ ์ด์์์ ๋ค์๊ณผ ๊ฐ์์ผ ํ๋ค.
my-app/ โโ .gitignore โโ images.d.ts โโ node_modules/ โโ public/ โโ src/ โ โโ ... โโ package.json โโ tsconfig.json โโ tsconfig.prod.json โโ tsconfig.test.json โโ tslint.json
-
- Selectors๋ ํ์๋ ๋ฐ์ดํฐ๋ฅผ ๊ณ์ฐํ์ฌ, Redux๊ฐ ๊ฐ๋ฅํ ์ต์์ state๋ฅผ ์ ์ฅํ ์ ์๋๋ก ํ๋ค.
- Selectors๋ ํจ์จ์ ์ด๋ค. selector๋ ์ธ์ ์ค ํ๋๋ ๋ณํ๊ฐ ์๋ค๋ฉด ์ฌ๊ณ์ฐ์ ํ์ง ์๋๋ค.
- Selectors๋ composableํ๋ค. ๋ค๋ฅธ selector์ ๋ํ ์ ๋ ฅ์ผ๋ก ์ฌ์ฉํ ์ ์๋ค.
-
Reselect์ ๊ฐ๋จํ ์ฌ์ฉ๋ฒ์ผ๋ก๋ ๋ค๋ฅธ ์๋์ ์ ์ ์ฃผ๋ฌธ์ ๊ณ์ฐํด๋ณด์.
import { createSelector } from 'reselect' const shopItemsSelector = state => state.shop.items const taxPercentSelector = state => state.shop.taxPercent const subtotalSelector = createSelector( shopItemsSelector, items => items.reduce((acc, item) => acc + item.value, 0) ) const taxSelector = createSelector( subtotalSelector, taxPercentSelector, (subtotal, taxPercent) => subtotal * (taxPercent / 100) ) export const totalSelector = createSelector( subtotalSelector, taxSelector, (subtotal, tax) => ({ total: subtotal + tax }) ) let exampleState = { shop: { taxPercent: 8, items: [ { name: 'apple', value: 1.20 }, { name: 'orange', value: 0.95 }, ] } } console.log(subtotalSelector(exampleState)) // 2.15 console.log(taxSelector(exampleState)) // 0.172 console.log(totalSelector(exampleState)) // { total: 2.322 }
-
Actions์ ์์ฉํ๋ก๊ทธ๋จ์์ store๋ก ๋ฐ์ดํฐ๋ฅผ ๋ณด๋ด๋ ์ผ๋ฐ JavaScript ๊ฐ์ฒด๋ ์ ๋ณด์ payload์ด๋ค. ์ด๊ฒ๋ค์ store์ ๋ํ ์ ์ผํ ์ ๋ณด ์์ค์ด๋ค. Actions์๋ ์ํ ์ค์ธ action ์ ํ์ ๋ํ๋ด๋ type ์์ฑ์ด ์์ด์ผ ํ๋ค.
์๋ก์ด ํ ์ผ ํญ๋ชฉ ์ถ๊ฐ๋ฅผ ๋ํ๋ด๋ ๋์์ ๋ํ ์์์ด๋ค.
{ type: ADD_TODO, text: 'Add todo item' }
-
์๋๋ค,
statics
์React.createClass()
์์๋ง ์๋ํ๋ค.someComponent= React.createClass({ statics: { someMethod: function() { // .. } } })
๊ทธ๋ฌ๋ ES6+ classes ์์์ static์ ์ฌ์ฉํ๊ฑฐ๋ ์๋์ ๊ฐ์ด ํด๋์ค ์ธ๋ถ์์ ์์ฑํ ์ ์๋ค.
class Component extends React.Component { static propTypes = { // ... } static someMethod() { // ... } }
class Component extends React.Component { .... } Component.propTypes = {...} Component.someMethod = function(){....}
-
Redux๋ ๋ชจ๋ UI ๊ณ์ธต์ ๋ฐ์ดํฐ ์ ์ฅ์๋ก ์ฌ์ฉํ ์ ์๋ค. ์ฃผ ์ฌ์ฉ์ฒ๋ React์ React Native์ด์ง๋ง, Angular, Angular 2, Vue, Mithril ๋ฑ์์ ์ฌ์ฉํ ์ ์๋ค. Redux๋ ๋ค๋ฅธ ์ฝ๋์์ ์ฌ์ฉํ ์ ์๋ subscription ๋ฉ์ปค๋์ฆ์ ์ ๊ณตํ๋ค.
-
Redux๋ ES6๋ก ์์ฑ๋์์ผ๋ฉฐ Webpack๊ณผ Babel์ ์ฌ์ฉํ์ฌ ES5๋ก ๋ณํ๋์ด์๋ค. JavaScript ๋น๋ ํ๋ก์ธ์ค์ ๊ด๋ จ ์์ด ์ฌ์ฉํ ์ ์์ด์ผ ํ๋ค. ๋ํ Redux๋ ๋น๋ ํ๋ก์ธ์ค ์์ด ์ง์ ์ฌ์ฉํ ์ ์๋ UMD ๋น๋๋ฅผ ์ ๊ณตํ๋ค.
-
enableReinitialize : true
์ค์ ์ ์ถ๊ฐํด์ผ ํ๋ค.const InitializeFromStateForm = reduxForm({ form: 'initializeFromState', enableReinitialize : true })(UserEdit)
initialValues
prop๊ฐ ์ ๋ฐ์ดํธ๋๋ฉด, form๋ ์ ๋ฐ์ดํธ๋๋ค. -
PropTypes
์oneOfType()
๋ฉ์๋๋ฅผ ์ฌ์ฉํ ์ ์๋ค.์๋ฅผ ๋ค์ด, height ์์ฑ์ ์๋์ ๊ฐ์ด
string
๋๋number
ํ์ ์ผ๋ก ์ ์๋ ์ ์๋ค.Component.PropTypes = { size: PropTypes.oneOfType([ PropTypes.string, PropTypes.number ]) }
-
SVG๋ฅผ ํ์ผ๋ก ๋ก๋ํ๋ ๋์ ์ ์ปดํฌ๋ํธ๋ก ์ง์ ๊ฐ์ ธ์ฌ ์ ์๋ค. ํด๋น ๊ธฐ๋ฅ์
react-scripts@2.0.0
๋๋ ๊ทธ ์ด์์์ ์ฌ์ฉ ๊ฐ๋ฅํ๋ค.import { ReactComponent as Logo } from './logo.svg' const App = () => ( <div> {/* Logo is an actual react component */} <Logo /> </div> )
Note: import์์ ์ค๊ดํธ๋ฅผ ์์ด์๋ ์๋๋ค.
-
ref ์ฝ๋ฐฑ์ด ์ธ๋ผ์ธ ํจ์๋ก ์ ์๋ ๊ฒฝ์ฐ ์ ๋ฐ์ดํธ ์ค์ ๋ ๋ฒ ํธ์ถ๋๋ค. (๋จผ์ null๋ก, DOM ์์๋ก ๋ค์ ํธ์ถ๋จ) ๋ ๋๋ง๋ง๋ค ํจ์์ ์ ์ธ์คํด์ค๊ฐ ์์ฑ๋๋ฏ๋ก React๋ ์ด์ ์ฐธ์กฐ๋ฅผ ์ง์ฐ๊ณ ์ ์ฐธ์กฐ๋ฅผ ์ค์ ํด์ผ ํ๊ธฐ ๋๋ฌธ์ด๋ค.
class UserForm extends Component { handleSubmit = () => { console.log("Input Value is: ", this.input.value) } render () { return ( <form onSubmit={this.handleSubmit}> <input type='text' ref={(input) => this.input = input} /> // Access DOM input in handle submit <button type='submit'>Submit</button> </form> ) } }
๊ทธ๋ฌ๋ ์ปดํฌ๋ํธ๊ฐ ๋ง์ดํธ๋ ๋ ref ์ฝ๋ฐฑ์ด ํ ๋ฒ ํธ์ถ๋๊ธฐ๋ฅผ ๊ธฐ๋ํ๋ค. ๋น ๋ฅธ ์์ ์ ES7 ํด๋์ค ๊ตฌ๋ฌธ์ ์ฌ์ฉํ์ฌ ํจ์๋ฅผ ์ ์ํ๋ ๊ฒ์ด๋ค.
```jsx
class UserForm extends Component {
handleSubmit = () => {
console.log("Input Value is: ", this.input.value)
}
setSearchInput = (input) => {
this.input = input
}
render () {
return (
<form onSubmit={this.handleSubmit}>
<input
type='text'
ref={this.setSearchInput} /> // Access DOM input in handle submit
<button type='submit'>Submit</button>
</form>
)
}
}
```
-
render hijacking์ ๊ฐ๋ ์ ์ปดํฌ๋ํธ๊ฐ ๋ค๋ฅธ ์ปดํฌ๋ํธ์์ ์ถ๋ ฅํ ๋ด์ฉ์ ์ ์ดํ๋ ๊ธฐ๋ฅ์ด๋ค. ์ค์ ๋ก ์ปดํฌ๋ํธ๋ฅผ Higher-Order component๋ก ๊ฐ์ธ์ ๋ํ๋ธ๋ค. ๊ฐ์ธ๊ฒ ๋๋ฉด ์ถ๊ฐ props ์ฃผ์ ํ๊ฑฐ๋ ๋ค๋ฅธ ๋ณ๊ฒฝ์ฌํญ์ ์ํํ์ฌ ๋ ๋๋ง ๋ ผ๋ฆฌ๊ฐ ๋ณ๊ฒฝ๋ ์ ์๋ค. ์ค์ ๋ก hijacking์ ์ฌ์ฉํ์ง ์์ง๋ง HOC๋ฅผ ์ฌ์ฉํ๋ฉด ์ปดํฌ๋ํธ๊ฐ ๋ค๋ฅธ ๋ฐฉ์์ผ๋ก ๋์ํ๊ฒ ๋๋ค.
-
React์์ HOC์ ๊ตฌํํ๋ ๋ฐฉ๋ฒ์ ํฌ๊ฒ 2๊ฐ์ง์ด๋ค. 1. Props Proxy (PP)์ 2. Inheritance Inversion (II). WrappedComponent๋ฅผ ์กฐ์ํ๊ธฐ ์ํ ๋ค์ํ ์ ๊ทผ๋ฒ์ด ์๋ค.
Props Proxy
์ด ์ ๊ทผ๋ฒ์์ HOC render ๋ฉ์๋๋ WrappedComponent ํ์ ์ React Element๋ฅผ ๋ฐํํ๋ค. ์ฐ๋ฆฌ๋ HOC๊ฐ ๋ฐ์ props๋ฅผ ์ ๋ฌํ๋ฏ๋ก, Props Proxy๋ผ๊ณ ๋ถ๋ฆฐ๋ค.
function ppHOC(WrappedComponent) { return class PP extends React.Component { render() { return <WrappedComponent {...this.props}/> } } }
Inheritance Inversion
์ด ์ ๊ทผ๋ฒ์์ ๋ฐํ๋ HOC ํด๋์ค(Enhancer)๊ฐ WrappedComponent๋ฅผ ํ์ฅํ๋ค. ์ผ๋ถ Enhancer ํด๋์ค๋ฅผ ํ์ฅํ๋ WrappedComponent ๋์ Enhancer์ ์ํด ์๋์ผ๋ก ํ์ฅ๋๋ฏ๋ก Inheritance Inversion์ด๋ผ๊ณ ๋ถ๋ฆฐ๋ค. ์ด๋ฌํ ๋ฐฉ์์ ํตํด ๊ทธ๋ค ์ฌ์ด์ ๊ด๊ณ๋ inverseํด์ง๋ค.
function iiHOC(WrappedComponent) { return class Enhancer extends WrappedComponent { render() { return super.render() } } }
-
์ค๊ดํธ({})๋ฅผ ํตํด์ ์ซ์๋ฅผ ์ ๋ฌํด์ผ ํ๋ค. ์ฌ๊ธฐ์ ๋ฌธ์์ด์ ๋ฐ์ดํ๋ก ๋ฌถ๋๋ค.
React.render(<User age={30} department={"IT"} />, document.getElementById('container'));
-
๊ฐ๋ฐ์์ ๊ฒฐ์ ์ ๋ฌ๋ ค์๋ค. ์ฆ ์์ฉ ํ๋ก๊ทธ๋จ์ ๊ตฌ์ฑํ๋ state์ ์ข ๋ฅ์ ๊ฐ state์ ์์น๋ฅผ ๊ฒฐ์ ํ๋ ๊ฒ์ ๊ฐ๋ฐ์์ ์์ ์ด๋ค. ์ผ๋ถ ์ฌ์ฉ์๋ ์ ํ๋ฆฌ์ผ์ด์ ์ ์ง๋ ฌํ ๋ฐ ์ ์ด ๊ฐ๋ฅํ ๋ฒ์ ์ ์ ์งํ๊ธฐ ์ํด Redux๋ก ๋ชจ๋ ๋จ์ผ ๋ฐ์ดํฐ๋ฅผ ์ ์งํ๋ ๊ฒ์ ์ ํธํ๋ค. ๋ค๋ฅธ ์ฌ๋๋ค์ ์ปดํฌ๋ํธ์ ๋ด๋ถ state๋ฅผ โ์ด ๋๋กญ๋ค์ด์ด ํ์ฌ ์ด๋ ค์์ต๋๊นโ์ ๊ฐ์ด ์ค์ํ์ง ์๊ฑฐ๋ UI state๋ฅผ ์ ์งํ๋ ๊ฒ์ ์ ํธํ๋ค.
์๋๋ ์ด๋ค ์ข ๋ฅ์ ๋ฐ์ดํฐ๋ฅผ Redux์ ๋ฃ์ด์ผ ํ๋์ง ๊ฒฐ์ ํ๋ ๊ท์น์ด๋ค.
- ์์ฉ ํ๋ก๊ทธ๋จ์ ๋ค๋ฅธ ๋ถ๋ถ์ด ๋ฐ์ดํฐ๋ฅผ ๊ด๋ฆฌํ๋?
- ์๋ณธ ๋ฐ์ดํฐ๋ฅผ ๊ธฐ๋ฐ์ผ๋ก ์ถ๊ฐ ํ์ ๋ฐ์ดํฐ๋ฅผ ์์ฑํ ์ ์์ด์ผ ํ๋?
- ์ฌ๋ฌ ์ปดํฌ๋ํธ๋ฅผ ๊ตฌ๋ํ๋๋ฐ ๋์ผํ ๋ฐ์ดํฐ๊ฐ ์ฌ์ฉ๋๋?
- ์ด state๋ฅผ ์ฃผ์ด์ง ์์ (์ฆ, ์๊ฐ์ฌํ ๋๋ฒ๊น )์ผ๋ก ๋ณต์ํ ์ ์๋ ๊ฐ์น๊ฐ ์๋?
- ๋ฐ์ดํฐ๋ฅผ ์บ์ ํ๊ณ ์ถ์๊ฐ(์ : ๋ฐ์ดํฐ๋ฅผ ๋ค์ ์์ฒญํ๋ ๋์ state๊ฐ ์๋ ๊ฒฝ์ฐ ์ฌ์ฉํ๋)?
-
React๋ ๊ธฐ๋ณธ์ ์ผ๋ก ๋ณ๋ค๋ฅธ ๊ตฌ์ฑ์์ด service worker๋ฅผ ์์ฑํ ์ ์๋ค. service worker๋ ์์ฐ ๋ฐ ๊ธฐํ ํ์ผ์ ์บ์ฑํ์ฌ ์ฌ์ฉ์๊ฐ ์คํ๋ผ์ธ ์ํ์ด๊ฑฐ๋ ๋คํธ์ํฌ ์๋๊ฐ ๋๋ฆฐ ๊ฒฝ์ฐ์๋ ํ๋ฉด์ ๋ณผ ์ ์๋๋ก ํด์ฃผ๋ ์น API์ด๋ค. ์ด๋ ๋ ๋์ ์ฌ์ฉ์ ๊ฒฝํ์ ๊ตฌ์ถํ๋๋ฐ ๋์์ด๋๋ค. ์ฌ์ดํธ์ ์คํ๋ผ์ธ ๊ธฐ๋ฅ์ ์ถ๊ฐํ ์ ์๋ค.
import React from 'react'; import ReactDOM from 'react-dom'; import App from './App'; import registerServiceWorker from './registerServiceWorker'; ReactDOM.render(<App />, document.getElementById('root')); registerServiceWorker();
-
PureComponent ๋๋ shouldComponentUpdate ์ฌ์ฉ์ ์ ๋ ฅ props๊ฐ ๋์ผํ๋ค๋ฉด, ํด๋์ค ์ปดํฌ๋ํธ์ ๋ ๋๋ง์ด ์ ํ๋ ์ ์๋ค. ์ด์ ๋ ํจ์ํ ์ปดํฌ๋ํธ๋ฅผ React.memo๋ก ๊ฐ์ธ์ ๋์ผํ ๊ธฐ๋ฅ์ ์ํํ ์ ์๋ค.
const MyComponent = React.memo(function MyComponent(props) { /* only rerenders if props change */ });
-
React.lazy ํจ์๋ฅผ ์ฌ์ฉํ๋ฉด dynamic import๋ก ์ผ๋ฐ ์ปดํฌ๋ํธ๋ฅผ ๋ ๋๋งํ ์ ์๋ค. ์ปดํฌ๋ํธ๊ฐ ๋ ๋๋ง ๋ ๋ OtherComponent๋ฅผ ํฌํจํ ๋ฒ๋ค์ ์๋์ผ๋ก ๋ก๋ํ๋ค. React ์ปดํฌ๋ํธ๋ฅผ ํฌํจํ ๊ธฐ๋ณธ ๋ด๋ณด๋ด๊ธฐ๊ฐ ์๋ ๋ชจ๋๋ก ํด์๋๋ Promise๋ฅผ ๋ฆฌํดํด์ผํ๋ค.
const OtherComponent = React.lazy(() => import('./OtherComponent')); function MyComponent() { return ( <div> <OtherComponent /> </div> ); }
Note: React.lazy ๋ฐ Suspense๋ ์์ง server-side ๋ ๋๋ง์ ์ฌ์ฉํ ์ ์๋ค. ์๋ฒ ๋ ๋๋ง ์ฑ์์ code-splitting์ ์ํํ๋ ค๋ ๊ฒฝ์ฐ ์ฌ์ ํ React Loadable์ด ๊ถ์ฅ๋๋ค.
-
state์ ํ์ฌ ๊ฐ๊ณผ ๊ธฐ์กด์ ๊ฐ์ ๋น๊ตํ์ฌ ๋ฆฌ๋ ๋๋ง์ ํ ์ง ๋ง์ง ๊ฒฐ์ ํ ์ ์๋ค. ๊ฐ์ด ๊ฐ๋ค๋ฉด null์ ๋ฐํํ์ฌ ๋ฆฌ๋ ๋๋ง์ ๋ฉ์ถ๊ณ ๊ทธ๋ ์ง ์์ผ๋ฉด ์ต์ ์ state ๊ฐ์ ๋ฐํํ๋ค. ์๋ฅผ ๋ค์ด, ์ฌ์ฉ์ ํ๋กํ ์ ๋ณด๋ ์๋์ ๊ฐ์ด ์กฐ๊ฑด๋ถ๋ก ๋ ๋๋ง์ด ์ด๋ฃจ์ด์ง๋ค.
getUserProfile = user => { const latestAddress = user.address; this.setState(state => { if (state.address === latestAddress) { return null; } else { return { title: latestAddress }; } }); };
-
Arrays: ์ด์ ๋ฆด๋ฆฌ์ฆ์ ๋ค๋ฅด๊ฒ, React 16์์ render ๋ฉ์๋๊ฐ ๋จ์ผ ์๋ฆฌ๋จผํธ๋ฅผ ๋ฐํํ ํ์๋ ์๋ค. ๋ฐฐ์ด์ ๋ฐํํจ์ผ๋ก์จ ๋ณ๋์ ๋ฌถ์ด์ฃผ๋ ์๋ฆฌ๋จผํธ ์์ด ์ฌ๋ฌ ํ์ ์์๋ฅผ ๋ฐํํ ์ ์๋ค. ์๋ฅผ ๋ค์ด, ์๋์ ๊ฐ๋ฐ์ ๋ชฉ๋ก์ ๋ณด์.
const ReactJSDevs = () => { return [ <li key="1">John</li>, <li key="2">Jackie</li>, <li key="3">Jordan</li> ]; }
๋ฐฐ์ด์ ๋ค๋ฅธ ๋ฐฐ์ด ์ปดํฌ๋ํธ์ ๋ณํฉํ ์ ์๋ค.
const JSDevs = () => { return ( <ul> <li>Brad</li> <li>Brodge</li> <ReactJSDevs/> <li>Brandon</li> </ul> ); }
Strings๊ณผ Numbers: render ๋ฉ์๋์์ ๋ฌธ์์ด๊ณผ ์ซ์ ํ์ ์ ๋ฐํํ ์ ์๋ค.
render() { return 'Welcome to ReactJS questions'; } // Number render() { return 2018; }
-
ํด๋์ค ์ ์ธ๋ฌธ์ ์ฌ์ฉํ์ฌ React Class ์ปดํฌ๋ํธ๋ฅผ ํจ์ฌ ๊ฐ๊ฒฐํ๊ฒ ๋ง๋ค ์ ์๋ค. ์์ฑ์๋ฅผ ์ฌ์ฉํ์ง ์๊ณ ์ง์ญ state๋ฅผ ์ด๊ธฐํํ ์ ์์ผ๋ฉฐ, ๋ณ๋์ ๋ฐ์ธ๋ฉ ์์ด ํ์ดํ ํจ์๋ฅผ ์ฌ์ฉํด์ ํด๋์ค ๋ฉ์๋๋ฅผ ์ ์ธํ ์ ์๋ค. ๋ฐ์ธ๋ฉ ์๋ ์์ฑ์์ ๋ฉ์๋๋ฅผ ์ฌ์ฉํ์ง ์๊ณ state์ ๋ํ ํด๋์ค ํ๋ ์ ์ธ์ ๋ณด์ฌ์ฃผ๋ ์นด์ดํฐ ์์ ๋ฅผ ๋ณด์.
class Counter extends Component { state = { value: 0 }; handleIncrement = () => { this.setState(prevState => ({ value: prevState.value + 1 })); }; handleDecrement = () => { this.setState(prevState => ({ value: prevState.value - 1 })); }; render() { return ( <div> {this.state.value} <button onClick={this.handleIncrement}>+</button> <button onClick={this.handleDecrement}>-</button> </div> ) } }
-
Hooks๋ ํด๋์ค๋ฅผ ์์ฑํ์ง ์๊ณ ๋ state์ ๋ค๋ฅธ React ๊ธฐ๋ฅ์ ์ฌ์ฉํ ์ ์๋ ์๋ก์ด ๊ธฐ๋ฅ์ด๋ค. useState hook ์์ ๋ฅผ ์ดํด๋ณด์.
import { useState } from 'react'; function Example() { // ์๋ก์ด state ๋ณ์๋ฅผ ์ ์ธํ๋ฉฐ, "count"๋ผ๊ณ ๋ถ๋ฆฐ๋ค. const [count, setCount] = useState(0); return ( <div> <p>You clicked {count} times</p> <button onClick={() => setCount(count + 1)}> Click me </button> </div> ); }
-
hooks๋ฅผ ์ฌ์ฉํ๊ธฐ ์ํด์ 2๊ฐ์ง ๊ท์น์ ๋ฐ๋ผ์ผํ๋ค.
- react ํจ์์ ์ต์์์์๋ง Hooks๋ฅผ ํธ์ถํ๋ค. ์๋ก, ๋ฐ๋ณต๋ฌธ, ์กฐ๊ฑด๋ฌธ ๋๋ ์ค์ฒฉ ํจ์๋ด์์ Hooks๋ฅผ ํธ์ถํ๋ฉด ์ ๋๋ค. ์ด๋ ๊ฒ ํ๋ฉด ์ปดํฌ๋ํธ๊ฐ ๋ ๋๋ง ๋ ๋๋ง๋ค Hooks๊ฐ ๋์ผํ ์์๋ก ํธ์ถ๋๊ณ , ์ฌ๋ฌ useState ๋ฐ useEffect ํธ์ถ๊ฐ์ Hooks state๊ฐ ๋ณด์ฅ๋๋ค.
- Hooks๋ React ํจ์์์๋ง ํธ์ถํ๋ค. ์๋ก, ์ผ๋ฐ์ ์ธ JavaScript ํจ์์์ Hooks๋ฅผ ํธ์ถํ๋ฉด ์ ๋๋ค.
-
React ํ์ ๋๊ฐ์ง ๊ท์น์ ์ ์ฉํ๋ eslint-plugin-react-hooks๋ผ๋ ESLint ํ๋ฌ๊ทธ์ธ์ ์ถ์ํ๋ค. ์๋์ ๋ช ๋ น์ ์ฌ์ฉํ์ฌ ํ๋ฌ๊ทธ์ธ์ ํ๋ก์ ํธ์ ์ถ๊ฐํ ์ ์๋ค.
npm install eslint-plugin-react-hooks@next
ESLint config ํ์ผ์ ์๋ ๊ตฌ์ฑ์ ์ ์ฉํ๋ค.
// Your ESLint configuration { "plugins": [ // ... "react-hooks" ], "rules": { // ... "react-hooks/rules-of-hooks": "error" } }
Note: ์ด ํ๋ฌ๊ทธ์ธ์ Create React App์์ ๊ธฐ๋ณธ๊ฐ์ผ๋ก ์ฌ์ฉํ๋๋ก ์ค๊ณ๋์ด์๋ค.
-
์๋์ Flux์ Redux์ ์ฃผ์ํ ์ฐจ์ด์ ์ด ์๋ค.
Flux Redux State๋ ๋ณํ๋ค. State์ ๋ถ๋ณํ๋ค. Store๋ state์ ๋ณ๊ฒฝ๋ก์ง์ ํฌํจํ๋ค. Store์ ๋ณ๊ฒฝ๋ก์ง์ ๋ถ๋ฆฌ๋์ด์๋ค. ๋ฉํฐ store๊ฐ ์๋ค. ํ๋์ store๋ง ์๋ค. ๋ชจ๋ stores๋ ์ฐ๊ฒฐ์ด ๋์ด์ง๊ณ ํํํ๋ค. ์์ง์ reducer๊ฐ ์๋ ๋จ์ผ store์ด๋ค. ์ฑ๊ธํค dispatcher๊ฐ ์๋ค. dispatcher ๊ฐ๋ ์ด ์๋ค. React ์ปดํฌ๋ํธ๋ store๋ฅผ ๊ตฌ๋ ํ๋ค. Container ์ปดํฌ๋ํธ๋ connect ํจ์๋ฅผ ์ฌ์ฉํ๋ค. -
๋ค์์ React Router V4 ๋ชจ๋์ ์ฃผ์ ์ฅ์ ์ด๋ค.
- React Router v4(version 4)์์ API๋ ์ปดํฌ๋ํธ์ ๊ด๋ จ๋ ๊ฒ์ด๋ค. ๋ผ์ฐํฐ๋ ํน์ ํ์ ๋ผ์ฐํฐ ์ปดํฌ๋ํธ(
<Route>
)๋ฅผ ๊ฐ์ธ๋ ๋จ์ธ ์ปดํฌ๋ํธ(<BrowserRouter>
)๋ก ์๊ฐํํ ์ ์๋ค. - history๋ฅผ ์๋์ผ๋ก ์ค์ ํ ํ์ ์๋ค. ๋ผ์ฐํฐ ๋ชจ๋์
<BrowserRouter>
์ปดํฌ๋ํธ๋ก ๊ฒฝ๋ก๋ฅผ ์ค์์ผ๋ก์จ history๋ฅผ ๊ด๋ฆฌํ๋ค. - ํน์ ๋ผ์ฐํฐ ๋ชจ๋(์น, ์ฝ์ด ๋๋ ๊ธฐ๋ณธ)๋ง ์ถ๊ฐํ์ฌ ์์ฉํ๋ก๊ทธ๋จ ํฌ๊ธฐ๊ฐ ์ค์๋ค.
- React Router v4(version 4)์์ API๋ ์ปดํฌ๋ํธ์ ๊ด๋ จ๋ ๊ฒ์ด๋ค. ๋ผ์ฐํฐ๋ ํน์ ํ์ ๋ผ์ฐํฐ ์ปดํฌ๋ํธ(
-
componentDidCatch ์๋ช ์ฃผ๊ธฐ ๋ฉ์๋๋ ํ์ ์ปดํฌ๋ํธ์์ ์ค๋ฅ๊ฐ ๋ฐ์ํ๋ฉด ํธ์ถ๋๋ค. ์ด ๋ฉ์๋๋ 2๊ฐ์ ๋งค๊ฐ๋ณ์๋ฅผ ๋ฐ๋๋ค.
- error: - ๋ณด๋ด์ง ์๋ฌ ๊ฐ์ฒด
- info: - ์ค๋ฅ๋ฅผ ๋ฐ์์ํจ ์ปดํฌ๋ํธ์ ๋ํ ์ ๋ณด๋ฅผ ํฌํจํ componentStack key๊ฐ ์๋ ๊ฐ์ฒด
๋ฉ์๋์ ๊ตฌ์กฐ๋ ์๋์ ๊ฐ๋ค.
componentDidCatch(error, info)
-
์๋์ ๊ฒฝ์ฐ๋ ์ค๋ฅ ๊ฒฝ๊ณ๊ฐ ์๋ํ์ง ์๋ ๊ฒฝ์ฐ์ด๋ค.
- Event handlers ์์ชฝ์ผ ๊ฒฝ์ฐ
- setTimeout์ด๋ requestAnimationFrame ์ฝ๋ฐฑ์ ์ฌ์ฉํ๋ ๋น๋๊ธฐ ์ฝ๋
- Server side rendering์ ํ ๊ฒฝ์ฐ
- ์ค๋ฅ ๊ฒฝ๊ณ ์ฝ๋ ์์ฒด์์ ์ค๋ฅ๊ฐ ๋ฐ์ํ ๊ฒฝ์ฐ
-
์๋ฌ ๋ฐ์ด๋๋ฆฌ๋ ์ด๋ฒคํธ ํธ๋ค๋ฌ๋ด์์ ์ค๋ฅ๋ฅผ ์ก์๋ด์ง ๋ชปํ๋ค. ๋ ๋๋ง ๋ฉ์๋๋ ๋ผ์ดํ ์ฌ์ดํด ๋ฉ์๋์ ๋ค๋ฅด๊ฒ ์ด๋ฒคํธ ํธ๋ค๋ฌ๋ ๋ ๋๋งํ๋ ๋์ ๋ฐ์ํ๊ฑฐ๋ ํธ์ถ๋์ง ์๋๋ค. ๋ฐ๋ผ์ React๋ ์ด๋ฒคํธ ํธ๋ค๋ฌ์์ ์ด๋ฌํ ์ข ๋ฅ์ ์ค๋ฅ๋ฅผ ๋ณต๊ตฌํ๋ ๋ฐฉ๋ฒ์ ์๊ณ ์๋ค. ๊ทธ๋๋ ์ด๋ฒคํธ ํธ๋ค๋ฌ๋ด์์ ์ค๋ฅ๋ฅผ ์ก์๋ด์ผ ํ๋ค๋ฉด, ์๋์ฒ๋ผ ์ผ๋ฐ Javascript try / catch ๋ฌธ์ ์ฌ์ฉํ๋ฉด ๋๋ค.
class MyComponent extends React.Component { constructor(props) { super(props); this.state = { error: null }; } handleClick = () => { try { // Do something that could throw } catch (error) { this.setState({ error }); } } render() { if (this.state.error) { return <h1>Caught an error.</h1> } return <div onClick={this.handleClick}>Click Me</div> } }
์์ ์ฝ๋๋ ์๋ฌ ๋ฐ์ด๋๋ฆฌ ๋์ ๋ฐ๋๋ผ ์๋ฐ์คํฌ๋ฆฝํธ try / catch ๋ธ๋ก์ ์ฌ์ฉํด์ ์ค๋ฅ๋ฅผ ์ก์๋ด๊ณ ์๋ค.
-
Try catch ๋ธ๋ก์ ๋ช ๋ น ์ฝ๋์ ํจ๊ป ์๋ํ์ง๋ง, ์๋ฌ ๋ฐ์ด๋๋ฆฌ๋ ์ ์ธ ์ฝ๋๊ฐ ํ๋ฉด์ ๋ ๋๋งํ ๋์ด๋ค. ์๋ฅผ ๋ค์ด, try catch ๋ธ๋ก์ ์๋ ๋ช ๋ น ์ฝ๋๊ฐ์ด ์ฌ์ฉ๋๋ค.
try { showButton(); } catch (error) { // ... }
๋ฐ๋ฉด ์๋ฌ ๋ฐ์ด๋๋ฆฌ๋ ์ ์ธ์ ์ฝ๋๋ฅผ ์๋์ ๊ฐ์ด ๊ฐ์ผ๋ค.
<ErrorBoundary> <MyComponent /> </ErrorBoundary>
๋ฐ๋ผ์ tree์ ์ด๋๊ฐ setState๋ก ์ธํด์ componentDidUpdate ๋ฉ์๋์์ ์ค๋ฅ๊ฐ ๋ฐ์ํ๋ฉด, ๊ฐ์ฅ ๊ฐ๊น์ด ์๋ฌ ๋ฐ์ด๋๋ฆฌ๋ก ์ ํ๋๋ค.
-
React 16์์ ์๋ฌ ๋ฐ์ด๋๋ฆฌ์ ์กํ์ง ์๋ ์ค๋ฅ๋ React ์ปดํฌ๋ํธ tree๋ฅผ ๋ง์ดํธ ํด์ ํ๋ค. ์ด๋ ๊ฒ ๋๋ ์ด์ ๋ ์์๋ UI๋ฅผ ์์ ํ ์ ๊ฑฐํ๋ ๊ฒ๋ณด๋ค ์์๋ UI๋ฅผ ๊ทธ๋๋ก ๋๋ ๊ฒ์ด ๋ ์ ์ข๊ธฐ ๋๋ฌธ์ด๋ค. ์๋ฅผ ๋ค์ด, ๊ฒฐ์ ์ฑ์ด ์๋ชป๋ ๊ธ์ก์ ํ๊ธฐํ๋ ๊ฒ๋ณด๋ค ๋ ๋๋งํ์ง ์๋ ๊ฒ์ด ๋ซ๋ค.
-
์๋ฌ ๋ฐ์ด๋๋ฆฌ์ ์ธ๋ถํ๋ ํ๋ก์ ํธ ํ์์ ๋ฐ๋ผ ๊ฐ๋ฐ์์ ๋ฐ๋ผ ๋ฌ๋ผ์ง ์ ์๋ค. ์๋์ ๊ฐ์ ์ ๊ทผ๋ฒ์ ์ํํ ์ ์๋ค.
- ์ต์์ ๋ผ์ฐํธ ์ปดํฌ๋ํธ๋ฅผ ๊ฐ์ธ์ ์ ์ฒด ์์ฉ ํ๋ก๊ทธ๋จ์ ๋ํ ์ผ๋ฐ์ ์ธ ์๋ฌ ๋ฉ์์ง๋ฅผ ํ์ํ ์ ์๋ค.
- ๊ฐ๊ฐ์ ์ปดํฌ๋ํธ๋ฅผ ์๋ฌ ๋ฐ์ด๋๋ฆฌ๋ก ๊ฐ์ธ์ ๋๋จธ์ง ์์ฉ ํ๋ก๊ทธ๋จ์ด ์ถฉ๋ํ์ง ์๋๋ก ๋ณดํธํ ์ ์๋ค.
-
์๋ฌ ๋ฉ์์ง, ์๋ฐ์คํฌ๋ฆฝํธ ์คํ๊ณผ ๋ณ๊ฐ๋ก, React16์ ์๋ฌ ๋ฐ์ด๋๋ฆฌ ๊ฐ๋ ์ ์ฌ์ฉํ์ฌ ํ์ผ ์ด๋ฆ๊ณผ ํ ๋ฒํธ๋ฅผ ํฌํจํ ์ปดํฌ๋ํธ ์คํ ์ถ์ ์ ํ์ํ๋ค. ์๋ฅผ ๋ค์ด, BuggyCounter ์ปดํฌ๋ํธ๋ ์๋์ ๊ฐ์ด ์ปดํฌ๋ํธ ์คํ ์ถ์ ์ ํ์ํ๋ค.
-
render() ๋ฉ์๋๋ ํด๋์ค ์ปดํฌ๋ํธ์์ ์ ์ผํ๊ฒ ํ์ํ ๋ฉ์๋์ด๋ค. ์ฆ, render ๋ฉ์๋๋ฅผ ์ ์ธํ ๋ชจ๋ ๋ฉ์๋๋ ํด๋์ค ์ปดํฌ๋ํธ์์ ์ ํ์ฌํญ์ด๋ค.
-
์๋๋ render ๋ฉ์๋์์ ์ฌ์ฉ๋๊ฑฐ๋ return ๋๋ ๋ฆฌ์คํธ์ ๋๋ค.
- React elements: React๊ฐ DOM ๋
ธ๋๋ฅผ ๋ ๋๋งํ๋๋ก ์ง์ํ๋ Elements๋ค.
<div/>
์ ๊ฐ์ html elements์ ์ฌ์ฉ์ ์ ์ elements๊ฐ ํฌํจ๋๋ค. - Arrays and fragments: ์ฌ๋ฌ elements๋ฅผ ๋ ๋๋งํ๊ธฐ ์ํด ์ด๋ฅผ ๊ฐ์ผ ๋ฐฐ์ด๊ณผ Fragments๋ฅผ ๋ฐํํ๋ค.
- Portals: ์์์ ๋ค๋ฅธ DOM ํ์ ํธ๋ฆฌ๋ก ๋ ๋๋งํ๋ค.
- String and numbers: DOM์์ ๋ฌธ์์ด๊ณผ ์ซ์๋ฅผ ๋ชจ๋ ํ ์คํธ ๋ ธ๋๋ก ๋ ๋๋งํ๋ค.
- Booleans or null: ์๋ฌด๊ฒ๋ ๋ ๋๋งํ์ง ์์ง๋ง, ์กฐ๊ฑด๋ถ๋ก ์ฝํ ์ธ ๋ฅผ ๋ ๋๋งํ ๋ ์ฌ์ฉ๋๋ค.
- React elements: React๊ฐ DOM ๋
ธ๋๋ฅผ ๋ ๋๋งํ๋๋ก ์ง์ํ๋ Elements๋ค.
-
constructor๋ ์ฃผ๋ก ๋ ๊ฐ์ง ๋ชฉ์ ์ผ๋ก ์ฌ์ฉ๋๋ค.
- this.state์ ๊ฐ์ฒด๋ฅผ ํ ๋นํ์ฌ ๋ก์ปฌ ์ํ๋ฅผ ์ด๊ธฐํํ๊ธฐ ์ํด์
- ์ด๋ฒคํธ ํธ๋ค๋ฌ ๋ฉ์๋๋ฅผ ์ธ์คํด์ค์ ๋ฐ์ธ๋ฉํ๊ธฐ ์ํด์
์๋ฅผ ๋ค์ด ์๋์ ์ฝ๋๋ ๋ ๊ฐ์ง ๊ฒฝ์ฐ๋ฅผ ๋ชจ๋ ๋ค๋ฃจ๊ณ ์๋ค.
constructor(props) { super(props); // Don't call this.setState() here! this.state = { counter: 0 }; this.handleClick = this.handleClick.bind(this); }
-
ํ์๋ ์๋๋ค. ์ฆ, state๋ฅผ ์ด๊ธฐํ ํ์ง ์๊ณ , ๋ฉ์๋๋ฅผ ๋ฐ์ธ๋ฉํ์ง ์๋๋ค๋ฉด, React ์ปดํฌ๋ํธ์ ์์ฑ์๋ฅผ ๊ตฌํํ ํ์ ์๋ค.
-
defaultProps๋ ์ปดํฌ๋ํธ ํด๋์ค์ ์์ฑ์ด๋ฉฐ ํด๋์ค์ ๊ธฐ๋ณธ props๋ฅผ ์ค์ ํ๋ค. undefined props์ ์ฌ์ฉ๋์ง๋ง, null props์๋ ์ฌ์ฉ๋์ง ์๋๋ค. ์๋ฅผ ๋ค์ด, ๋ฒํผ ์ปดํฌ๋ํธ์ ์์ default prop ๋ง๋ค์ด๋ณด์.
class MyButton extends React.Component { // ... } MyButton.defaultProps = { color: 'red' };
props.color๊ฐ ์๋ค๋ฉด ๊ธฐ๋ณธ๊ฐ์ '๋นจ๊ฐ์'์ผ๋ก ์ค์ ๋๋ค. ์ฆ, color props์ ์ ๊ทผํ๋ ค๊ณ ํ ๋๋ง๋ค ๊ธฐ๋ณธ๊ฐ์ด ์ฌ์ฉ๋๋ค.
render() { return <MyButton /> ; // props.color will be set to red }
Note: null ๊ฐ์ ์ ๊ณตํ๋ฉด null ๊ฐ์ด ์ ์ง๋๋ค.
-
์ปดํฌ๋ํธ ์ธ์คํด์ค๊ฐ ๋ง์ดํธ ํด์ ๋๋ฉด ๋ค์ ๋ง์ดํธ๋ ์ ์์์ผ๋ก, componentWillUnmount()์์ setState()๋ฅผ ํธ์ถํ๋ฉด ์ ๋๋ค.
-
์ด ๋ผ์ดํ์ฌ์ดํด ๋ฉ์๋๋ ํ์ ์ปดํฌ๋ํธ๊ฐ ์ค๋ฅ๋ฅผ ๋ฐ์ํ ํ ํธ์ถ๋๋ค. ๋งค๊ฐ ๋ณ์๋ก ์ค๋ฅ๋ฅผ ์์ ํ๊ณ state๋ฅผ ์ ๋ฐ์ดํธํ๊ธฐ ์ํ ๊ฐ์ ๋ฐํํ๋ค. ๋ผ์ดํ์ฌ์ดํด ๋ฉ์๋์ ์ฌ์ฉ๋ฒ์ ์๋์ ๊ฐ๋ค.
static getDerivedStateFromError(error)
์์ ๋ผ์ดํ์ฌ์ดํด ๋ฉ์๋๋ฅผ ์ฌ์ฉํ์ฌ ์ค๋ฅ ๋ฐ์ด๋๋ฆฌ ์ฌ์ฉ ์์๋ ์๋์ ๊ฐ์ต๋๋ค.
class ErrorBoundary extends React.Component { constructor(props) { super(props); this.state = { hasError: false }; } static getDerivedStateFromError(error) { // Update state so the next render will show the fallback UI. return { hasError: true }; } render() { if (this.state.hasError) { // You can render any custom fallback UI return <h1>Something went wrong.</h1>; } return this.props.children; } }
-
props ๋๋ state์ ๋ณ๊ฒฝ์ผ๋ก ์ ๋ฐ์ดํธ๊ฐ ๋ฐ์ํ ์ ์๋ค. ์๋์ ๋ฉ์๋๋ ์ปดํฌ๋ํธ๋ฅผ ๋ฆฌ๋ ๋๋งํ ๋ ํธ์ถ๋๋ ์์์ด๋ค.
- static getDerivedStateFromProps()
- shouldComponentUpdate()
- render()
- getSnapshotBeforeUpdate()
- componentDidUpdate()
-
์๋๋ ๋ฉ์๋๋ ๋ ๋๋ง ๋์ค ๋ผ์ดํ์ฌ์ดํด ๋ฉ์๋ ๋๋ ํ์ ์ปดํฌ๋ํธ์ ์์ฑ์์์ ์ค๋ฅ๊ฐ ๋ฐ์ํ๋ฉด ํธ์ถ๋๋ค.
- static getDerivedStateFromError()
- componentDidCatch()
-
displayName ๋ฌธ์์ด์ ๋ฉ์์ง ๋๋ฒ๊น ์ ์ฌ์ฉ๋๋ค. ์ผ๋ฐ์ ์ผ๋ก ์ปดํฌ๋ํธ๋ฅผ ์ ์ํ๋ ํจ์ ๋๋ ํด๋์ค ์ด๋ฆ์์ ์ ์ถ๋๋ฏ๋ก ๋ช ์์ ์ผ๋ก ์ค์ ํ ํ์ ์๋ค. ๋๋ฒ๊น ๋ชฉ์ ์ผ๋ก ๋ค๋ฅธ ์ด๋ฆ์ ํ์ํ๊ฑฐ๋ HOC๋ฅผ ์์ฑํ ๋ ๋ช ์์ ์ผ๋ก ์ค์ ํ ์ ์๋ค. ์๋ฅผ ๋ค์ด, ๋๋ฒ๊น ์ ์ฝ๊ฒ ํ๋ ค๋ฉด withSubscription HOC์ ๊ฒฐ๊ณผ์์ ์๋ฆฌ๋ displayName์ ์ ํํ๋ค.
function withSubscription(WrappedComponent) { class WithSubscription extends React.Component {/* ... */} WithSubscription.displayName = `WithSubscription(${getDisplayName(WrappedComponent)})`; return WithSubscription; } function getDisplayName(WrappedComponent) { return WrappedComponent.displayName || WrappedComponent.name || 'Component'; }
-
React๋ Internet Explorer 9 ์ด์์ ํฌํจํ์ฌ ๋๋ฆฌ ์ฌ์ฉ๋๋ ๋ชจ๋ ๋ธ๋ผ์ฐ์ ๋ฅผ ์ง์ํ์ง๋ง, ์ผ๋ถ IE 9 ๋ฐ IE 10๊ณผ ๊ฐ์ ์ด์ ๋ธ๋ผ์ฐ์ ์์๋ ํด๋ฆฌํ์ด ํ์ํ๋ค. es5-shim๊ณผ es5-sham ํด๋ฆฌํ์ ์ฌ์ฉํ๋ฉด ES5 ๋ฉ์๋๋ค์ ์ง์ํ์ง ์๋ ์ด์ ๋ธ๋ผ์ฐ์ ์์ ์ฌ์ฉ ๊ฐ๋ฅํ๋ค.
-
์ด ๋ฉ์๋๋ react-dom ํจํค์ง์์ ์ฌ์ฉ ๊ฐ๋ฅํ๋ฉฐ, DOM์ ๋ง์ดํธ๋ React ์ปดํฌ๋ํธ๋ฅผ ์ ๊ฑฐํ๊ณ ์ด๋ฒคํธ ํธ๋ค๋ฌ์ state๋ฅผ ์ ๋ฆฌํ๋ค. ์ปจํ ์ด๋์ ์ปดํฌ๋ํธ๊ฐ ๋ง์ดํธ๋์ง ์์ ๊ฒฝ์ฐ ์ด ํจ์๋ฅผ ํธ์ถํ๋ฉด ์๋ฌด ์์ ๋ ํ์ง ์๋๋ค. ์ปดํฌ๋ํธ๊ฐ ๋ง์ดํธ ํด์ ๋ ๊ฒฝ์ฐ true, ๋ง์ดํธ ํด์ ํ ์ปดํฌ๋ํธ๊ฐ ์๋ ๊ฒฝ์ฐ false๋ฅผ ๋ฐํํ๋ค. ๋ฉ์๋ ์ฌ์ฉ๋ฒ์ ๋ค์๊ณผ ๊ฐ๋ค.
ReactDOM.unmountComponentAtNode(container)
-
Code-Splitting์ Webpack๊ณผ Browserify์ ๊ฐ์ ๋ฒ๋ค๋ฌ์์ ์ง์ํ๋ ๊ธฐ๋ฅ์ผ๋ก ๋ฐํ์์ ๋์ ์ผ๋ก ๋ก๋ํ ์ ์๋ ์ฌ๋ฌ ๋ฒ๋ค์ ๋ง๋ค ์ ์๋ค. react ํ๋ก์ ํธ๋ ๋์ import() ๊ธฐ๋ฅ์ ํตํด code splitting์ ์ง์ํ๋ค. ์๋ฅผ ๋ค์ด, ์๋์ ๊ฐ์ ์ค๋ํ ์ฝ๋์์๋ moduleA.js์ ๋ชจ๋ ๊ณ ์ ํ ์ข ์์ฑ์ ์ฌ์ฉ์๊ฐ '๋ก๋' ๋ฒํผ์ ํด๋ฆญํ์ ๋ ๋ก๋ํ๋ ๋ณ๋์ chuck๋ก ๋ง๋ ๋ค.
moduleA.js
const moduleA = 'Hello'; export { moduleA };
App.js
import React, { Component } from 'react'; class App extends Component { handleClick = () => { import('./moduleA') .then(({ moduleA }) => { // Use moduleA }) .catch(err => { // Handle failure }); }; render() { return ( <div> <button onClick={this.handleClick}>Load</button> </div> ); } } export default App;
-
<StrictMode>
๋ ์๋์ ๊ฐ์ ๊ฒฝ์ฐ์ ์ ์ฉํ๋ค.- ์์ ํ์ง ์์ ๋ผ์ดํ์ฌ์ดํด ๋ฉ์๋๋ฅผ ๊ฐ์ง ์ปดํฌ๋ํธ๋ฅผ ์๋ณํด์ค๋ค.
- ๋ ๊ฑฐ์ ๋ฌธ์์ด ์ฐธ์กฐ API ์ฌ์ฉ์ ๋ํ ๊ฒฝ๊ณ ๋ฅผ ํด์ค๋ค.
- ์์์น ๋ชปํ ๋ถ์์ฉ์ ๊ฐ์งํ๋ค.
- ๋ ๊ฑฐ์ ์ปจํ ์คํธ API๋ฅผ ๊ฐ์งํ๋ค.
- ๋ ์ด์ ์ฌ์ฉ๋์ง ์๋ findDOMNode ์ฌ์ฉ์ ๋ํด ๊ฒฝ๊ณ ๋ฅผ ํด์ค๋ค.
-
๋ช ์์
<React.Fragment>
๊ตฌ๋ฌธ์ผ๋ก ์ ์ธ๋ Fragments์๋ key๊ฐ ์์ ์ ์๋ค. ์ผ๋ฐ์ ์ผ๋ก ์ฌ์ฉํ๋ ๊ฒฝ์ฐ๋ ์๋์ ๊ฐ์ด ์ปฌ๋ ์ ์ ๋ฐฐ์ด์ ๋งคํํ๋ค.function Glossary(props) { return ( <dl> {props.items.map(item => ( // Without the `key`, React will fire a key warning <React.Fragment key={item.id}> <dt>{item.term}</dt> <dd>{item.description}</dd> </React.Fragment> ))} </dl> ); }
Note: key๋ Fragment์ ์ ๋ฌํ ์ ์๋ ์ ์ผํ ์์ฑ์ด๋ค. ์์ผ๋ก๋, ์ด๋ฒคํธ ํธ๋ค๋ฌ์ ๊ฐ์ ์ถ๊ฐ ์์ฑ์ด ์ง์๋ ์ ์๋ค.
-
React 16 ๋ฒ์ ์์, ํ์ค๊ณผ ์ฌ์ฉ์ ์ ์ DOM ์์ฑ ๋ชจ๋ ์ง์ํ๋ค. React ์ปดํฌ๋ํธ๋ ์ข ์ข ์ฌ์ฉ์ ์ ์์ DOM ๊ด๋ จ props๋ฅผ ๋ชจ๋ ์ฌ์ฉํ๋ฏ๋ก, React๋ DOM API์ ๋ง์ฐฌ๊ฐ์ง๋ก camelCase ๊ท์น์ ์ฌ์ฉํ๋ค. ํ์ค HTML ์์ฑ์ ์ฌ์ฉํ๊ณ props๋ฅผ ์ฌ์ฉํ์ง ์์ ์์์ด๋ค.
<div tabIndex="-1" /> // Just like node.tabIndex DOM API <div className="Button" /> // Just like node.className DOM API <input readOnly={true} /> // Just like node.readOnly DOM API
props๋ ํน๋ณํ ๊ฒฝ์ฐ๋ ์ ์ธํ๊ณ ํด๋น HTML ์์ฑ๊ณผ ์ ์ฌํ๊ฒ ์๋ํ๋ค. ๋ํ ๋ชจ๋ SVG ์์ฑ์ ์ง์ํ๋ค.
-
๊ณ ์ฐจ ์ปดํฌ๋ํธ๋ ์ฅ์ ๊ณผ๋ ๋ฌด๊ดํ๊ฒ ๋ช ๊ฐ์ง ๊ฒฝ๊ณ ์ฌํญ์ด ์์ต๋๋ค.
-
render ๋ฉ์๋ ์์์ ์ฌ์ฉํด์๋ ์ ๋๋ค. : ์ปดํฌ๋ํธ์ redner ๋ฉ์๋ ๋ด์์ HOC๋ฅผ ์ ์ฉํ๋ ๊ฒ์ ์ข์ง ์๋ค.
render() { // ์๋ก์ด ๋ฒ์ ์ EnhancedComponent๊ฐ ๋ ๋๋งํ ๋๋ง๋ค ์์ฑ๋๋ค. // EnhancedComponent1 !== EnhancedComponent2 const EnhancedComponent = enhance(MyComponent); // That causes the entire subtree to unmount/remount each time! return <EnhancedComponent />; }
์์ ์ฝ๋๋ ์ปดํฌ๋ํธ๋ฅผ ๋ค์ ๋ง์ดํธํ๋ฉด ํด๋น ์ปดํฌ๋ํธ ๋ฐ ๋ชจ๋ ํ์ ์ปดํฌ๋ํธ์ state๋ฅผ ์์ด๋ฒ๋ ค ์ฑ๋ฅ์ ์ํฅ์ ๋ฏธ์น๋ค. ๋์ ์ปดํฌ๋ํธ๊ฐ ํ ๋ฒ๋ง ์์ฑ๋๋๋ก ์ปดํฌ๋ํธ ์ ์ ์ธ๋ถ์์ HOC๋ฅผ ์ ์ฉํ๋ฉด ๋๋ค.
-
static ๋ฉ์๋๋ฅผ ๋ณต์ฌํด์ผ ํ๋ค. : HOC๋ฅผ ์๋ก์ด ์ปดํฌ๋ํธ์ ์ ์ฉํ ๋ ์๋ก์ด ์ปดํฌ๋ํธ์๋ ์๋ณธ ์ปดํฌ๋ํธ์ static ๋ฉ์๋๊ฐ ์๋ค.
// staic ๋ฉ์๋ ์ ์ WrappedComponent.staticMethod = function() {/*...*/} // HOC ์ ์ฉ const EnhancedComponent = enhance(WrappedComponent); // enhanced component๋ static ๋ฉ์๋๋ฅผ ๊ฐ์ง๊ณ ์์ง ์๋ค. typeof EnhancedComponent.staticMethod === 'undefined' // true
๋ฐํํ๊ธฐ ์ ์ ๋ฉ์๋๋ฅผ ์ปจํ ์ด๋์ ๋ณต์ฌํ์ฌ ํด๊ฒฐํ ์ ์๋ค.
function enhance(WrappedComponent) { class Enhance extends React.Component {/*...*/} // Must know exactly which method(s) to copy :( Enhance.staticMethod = WrappedComponent.staticMethod; return Enhance; }
-
Refs๊ฐ ์ ๋ฌ๋์ง ์๋๋ค. : HOC์ ๊ฒฝ์ฐ ๋ชจ๋ props๋ฅผ ๋ํ ๋ ์ปดํฌ๋ํธ๋ก ์ ๋ฌํด์ผ ํ์ง๋ง refs๋ ์ ์ฉ๋์ง ์๋๋ค. ref๋ ์ค์ ๋ก key์ ๋น์ทํ props์ด ์๋๊ธฐ ๋๋ฌธ์ด๋ค. ์ด ๊ฒฝ์ฐ์๋ React.forwardRef API ์ฌ์ฉํ๋ฉด ๋๋ค.
-
-
React.forwardRef ๋ ๋ ๋๋ง ํจ์๋ฅผ ๋งค๊ฐ ๋ณ์๋ก ๋ฐ๊ณ ์์ผ๋ฉฐ, ๊ฐ๋ฐ์ ๋๊ตฌ๋ ์ด ํจ์๋ฅผ ์ฌ์ฉํ์ฌ ref ์ ๋ฌ ์ปดํฌ๋ํธ์ ํ์ํ ๋ด์ฉ์ ๊ฒฐ์ ํ๋ค. ์๋ฅผ ๋ค์ด, ๋ ๋๋ง ํจ์์ ์ด๋ฆ์ ์ง์ ํ์ง ์๊ฑฐ๋ displayName ์์ฑ์ ์ฌ์ฉํ์ง ์์ผ๋ฉด ๊ฐ๋ฐ์ ๋๊ตฌ์์ โForwardRefโ๋ก ํ์๋๋ค.
const WrappedComponent = React.forwardRef((props, ref) => { return <LogProps {...props} forwardedRef={ref} />; });
๊ทธ๋ฌ๋ ๋ ๋๋ง ํจ์์ ์ด๋ฆ์ ์ง์ ํ๊ฒ ๋๋ฉด **โForwardRef(myFunction)โ**์ผ๋ก ๋ํ๋๋ค.
const WrappedComponent = React.forwardRef( function myFunction(props, ref) { return <LogProps {...props} forwardedRef={ref} />; } );
๋์์ผ๋ก, forwardRef ํจ์์ displayName ์์ฑ์ ์ค์ ํ ์ ์๋ค.
function logProps(Component) { class LogProps extends React.Component { // ... } function forwardRef(props, ref) { return <LogProps {...props} forwardedRef={ref} />; } // Give this component a more helpful display name in DevTools. // e.g. "ForwardRef(logProps(MyComponent))" const name = Component.displayName || Component.name; forwardRef.displayName = `logProps(${name})`; return React.forwardRef(forwardRef); }
-
prop์ ์๋ฌด ๊ฐ๋ ์ ๋ฌํ์ง ์๋๋ค๋ฉด, ๊ธฐ๋ณธ๊ฐ์ true์ด๋ค. ์ด ๋์์ HTML์ ๋์๊ณผ ์ผ์นํ๊ฒ ํ ์ ์๋ค. ์๋ฅผ ๋ค์ด ์๋์ ๊ฐ์ ํํ์์ ๋์ผํ ํํ์ด๋ค.
<MyInput autocomplete /> <MyInput autocomplete={true} />
Note: ์ด ๋ฐฉ๋ฒ์ ES6 ๊ฐ์ฒด ์๊ธฐ (์, {name: name}์ ์ฝ์๋ {name})์ ํผ๋ํ ์ ์์ผ๋ฏ๋ก ์ฌ์ฉํ์ง ์๋ ๊ฒ์ด ์ข๋ค.
-
Next.js๋ React๋ก ๋ง๋ค์ด์ง ์ ์ , ์๋ฒ ๋ ๋๋ง ์์ฉํ๋ก๊ทธ๋จ์ ์ํ ๋์ค์ ์ด๊ณ ๊ฐ๋ฒผ์ด ํ๋ ์ ์ํฌ์ด๋ค. ๋ํ ์คํ์ผ๋ง๊ณผ ๋ผ์ฐํ ์๋ฃจ์ ์ ์ ๊ณตํ๋ค. ์๋๋ NextJS๊ฐ ์ ๊ณตํ๋ ์ฃผ์ ๊ธฐ๋ฅ์ด๋ค.
- ๊ธฐ๋ณธ์ ์ผ๋ก ์๋ฒ ๋ ๋๋ง
- ๋น ๋ฅธ ํ์ด์ง ๋ก๋ฉ์ ์ํ ์๋ ์ฝ๋ ๋ถํ
- ๊ฐ๋จํ ํด๋ผ์ด์ธํธ ์ฌ์ด๋ ๋ผ์ฐํ (page๊ธฐ๋ฐ)
- HMR์ ์ง์ํ๋ ์นํฉ ๊ธฐ๋ฐ ๊ฐ๋ฐ ํ๊ฒฝ
- Express ๋๋ ๋ค๋ฅธ Node.js HTTP ์๋ฒ๋ก ๊ตฌํํ ์ ์๋ค.
- ๊ณ ์ ํ Babel ๋ฐ Webpack ๊ตฌ์ฑ์ผ๋ก ์ฌ์ฉ์ ์ ์๊ฐ ๊ฐ๋ฅํ๋ค.
-
์ด๋ฒคํธ ํธ๋ค๋ฌ ๋ฐ ํจ์๋ฅผ ํ์ ์ปดํฌ๋ํธ์ props๋ก ์ ๋ฌํ ์ ์๋ค. ์๋์ ๊ฐ์ด ์์ ์ปดํฌ๋ํธ์์ ์ฌ์ฉํ ์ ์๋ค.
<button onClick={this.handleClick}>
-
์, ์ฌ์ฉํ ์ ์๋ค. ์ฝ๋ฐฑ ํจ์์ ๋งค๊ฐ ๋ณ์๋ฅผ ์ ๋ฌํ๋ ๊ฐ์ฅ ์ฌ์ด ๋ฐฉ๋ฒ์ด๋ค. ๊ทธ๋ฌ๋ ์ฌ์ฉํ๋ ๋์ ์ฑ๋ฅ์ ์ต์ ํํด์ผ ํ๋ค.
class Foo extends Component { handleClick() { console.log('Click happened'); } render() { return <button onClick={() => this.handleClick()}>Click Me</button>; } }
Note: render ๋ฉ์๋์์ ํ์ดํ ํจ์๋ฅผ ์ฌ์ฉํ๋ฉด ์ปดํฌ๋ํธ๊ฐ ๋ ๋๋ง ๋ ๋๋ง๋ค ์๋ก์ด ๊ธฐ๋ฅ์ด ์์ฑ๋์ด ์ฑ๋ฅ์ ์ํฅ์ ์ค ์ ์๋ค.
-
onClick ๋๋ onScroll๊ณผ ๊ฐ์ ์ด๋ฒคํธ ํธ๋ค๋ฌ๋ฅผ ์ฌ์ฉํ๊ณ ์ฝ๋ฐฑ์ด ๋๋ฌด ๋นจ๋ฆฌ ๋ฐ์ํ์ง ์๋๋ก ํ๋ ค๋ฉด ์ฝ๋ฐฑ ์คํ ์๋๋ฅผ ์ ํํ ์ ์๋ค. ์๋์ ๊ฐ์ ๋ฐฉ๋ฒ์ผ๋ก ๊ฐ๋ฅํ๋ค.
- Throttling: ์๊ฐ์ ๊ธฐ๋ฐ์ผ๋ก ๋น๋์ ๋ฐ๋ผ ๋ณ๊ฒฝ์ด ๋๋ค. ์๋ฅผ ๋ค์ด, lodash์ _.throttle ํจ์๋ฅผ ์ฌ์ฉํ์ฌ ์ฌ์ฉํ ์ ์๋ค.
- Debouncing: ์ผ์ ๊ธฐ๊ฐ ์ฌ์ฉํ์ง ์์ผ๋ฉด ๋ณ๊ฒฝ ์ฌํญ์ ์คํํ๋ค. ์๋ฅผ ๋ค์ด, lodash์ _.debounce ํจ์๋ฅผ ์ฌ์ฉํ์ฌ ์ฌ์ฉํ ์ ์๋ค.
- RequestAnimationFrame throttling: requestAnimationFrame์ ๋ฐ๋ผ ๋ณ๊ฒฝ๋๋ค. ์๋ฅผ ๋ค์ด, lodash์ raf-schd ํจ์๋ฅผ ์ฌ์ฉํ์ฌ ์ฌ์ฉํ ์ ์๋ค.
-
React DOM์ ๋ ๋๋งํ๊ธฐ ์ ์ JSX์ ํฌํจ๋ ๋ชจ๋ ๊ฐ์ escape ์ฒ๋ฆฌํ๋ค. ๋ฐ๋ผ์ ์ ํ๋ฆฌ์ผ์ด์ ์ ์๋ฌด๊ฒ๋ ์ฃผ์ ๋์ง ์๋ ๊ฒ์ ๋ณด์ฅํ๋ค. ๋ชจ๋ ๊ฒ์ด ๋ ๋๋ง๋๊ธฐ ์ ์ ๋ฌธ์์ด๋ก ๋ณํ๋๋ค. ์๋ฅผ ๋ค์ด ์๋์ ๊ฐ์ด ์ฌ์ฉ์ ์ ๋ ฅ์ ํฌํจํ ์ ์๋ค.
const name = response.potentiallyMaliciousInput; const element = <h1>{name}</h1>;
์ด๋ ๊ฒ ํ๋ฉด ์ ํ๋ฆฌ์ผ์ด์ ์์ XSS(Cross-site-scripting) ๊ณต๊ฒฉ์ ๋ฐฉ์งํ ์ ์๋ค.
-
์๋ก ์์ฑ๋ ์์๋ฅผ ReactDOM์ render ๋ฉ์๋์ ์ ๋ฌํ์ฌ UI(๋ ๋๋ง ๋ ์์๋ก ํ์)๋ฅผ ์ ๋ฐ์ดํธ ํ ์ ์๋ค. ์๋ฅผ ๋ค์ด, ๋๋ฑ๊ฑฐ๋ฆฌ๋ ์๊ณ๋ฅผ ์๋ก ๋ค์๋ฉด, render ๋ฉ์๋๋ฅผ ์ฌ๋ฌ ๋ฒ ํธ์ถํ์ฌ ์๊ฐ์ ์ ๋ฐ์ดํธํ๋ค.
function tick() { const element = ( <div> <h1>Hello, world!</h1> <h2>It is {new Date().toLocaleTimeString()}.</h2> </div> ); ReactDOM.render(element, document.getElementById('root')); } setInterval(tick, 1000);
-
์ปดํฌ๋ํธ๋ฅผ ํจ์๋ ํด๋์ค๋ก ์ ์ธํ ๋ ์์ ์ props๋ฅผ ์์ ํด์๋ ์ ๋๋ค. ์๋์ capital ํจ์๋ฅผ ์๋ก ๋ค์ด๋ณด์.
function capital(amount, interest) { return amount + interest; }
์์ ํจ์๋ ์ ๋ ฅ์ ๋ณ๊ฒฝํ์ง ์์ผ๋ฉด์ ํญ์ ๋์ผํ ์ ๋ ฅ์ ๋ํด ๋์ผํ ๊ฒฐ๊ณผ๋ฅผ ๋ฐํํ๊ธฐ ๋๋ฌธ์ "์์"ํ๋ค๊ณ ํ๋ค. ๋ฐ๋ผ์, React์๋ "๋ชจ๋ React ์ปดํฌ๋ํธ๋ props์ ๊ด๋ จ๋ ๊ฒ์ ์์ํจ์์ฒ๋ผ ์๋ํด์ผ ํ๋ค." ๋ผ๋ ๋จ์ผ ๊ท์น์ ๊ฐ์ง๊ณ ์๋ค.
-
์ปดํฌ๋ํธ์์ setState()๋ฅผ ์คํํ๋ฉด, React ์ฌ์ฉ์๊ฐ ์ ๊ณตํ object๋ฅผ ํ์ฌ state๋ก ๋ณํฉํ๋ค. ์๋ฅผ ๋ค์ด, ๊ฒ์๋ฌผ ๋ฐ ๋๊ธ ์ธ๋ถ ์ ๋ณด๋ฅผ state ๋ณ์๋ก ์ฌ์ฉํ๋ ํ์ด์ค๋ถ ์ฌ์ฉ์๋ฅผ ์ดํด๋ณด์.
constructor(props) { super(props); this.state = { posts: [], comments: [] }; }
์๋์ ๊ฐ์ด ๋ณ๋์ setState()๋ฅผ ํธ์ถํ์ฌ ๋ ๋ฆฝ์ ์ผ๋ก ์ ๋ฐ์ดํธํ ์ ์๋ค.
componentDidMount() { fetchPosts().then(response => { this.setState({ posts: response.posts }); }); fetchComments().then(response => { this.setState({ comments: response.comments }); }); }
์์ ์ฝ๋ ์ค๋ํ์์ ์ธ๊ธํ๋ฏ์ด this.setState({comments})๋ ๊ฒ์๋ฌผ ๋ณ์๋ฅผ ์์ ํ๊ฑฐ๋ ๋ฐ๊พธ์ง ์๊ณ comments ๋ณ์๋ง ์ ๋ฐ์ดํธํ๋ค.
-
๋ฐ๋ณต ๋๋ ๋ฃจํ๋ฅผ ๋๋ ์ค์ ์ถ๊ฐ์ ์ธ ๋งค๊ฐ ๋ณ์๋ฅผ ์ด๋ฒคํธ ํธ๋ค๋ฌ์ ์ ๋ฌํ๋ ๊ฒ์ด ์ผ๋ฐ์ ์ด๋ค. ํ์ดํ ํจ์ ๋๋ ๋ฉ์๋ ๋ฐ์ธ๋๋ก ๊ฐ๋ฅํ๋ค. ๊ทธ๋ฆฌ๋์์ ์ ๋ฐ์ดํธ๋ ์ฌ์ฉ์ ์ ๋ณด๋ก ์๋ฅผ ๋ค์ด ๋ณด์.
<button onClick={(e) => this.updateUser(userId, e)}>Update User details</button> <button onClick={this.updateUser.bind(this, userId)}>Update User details</button>
๋ ๊ฐ์ง ์ ๊ทผ๋ฐฉ์์์, ํฉ์ฑ ์ธ์ e๋ ๋ ๋ฒ์งธ ์ธ์๋ก ์ ๋ฌ๋๋ค. ํ์ดํ ํจ์์ ๊ฒฝ์ฐ์๋ ๋ช ์์ ์ผ๋ก ์ ๋ฌํด ์ฃผ์ด์ผ ํ๋ฉฐ, ๋ฉ์๋ ๋ฐ์ธ๋๋ ์๋์ผ๋ก ์ ๋ฌ๋๋ค.
-
ํน์ ์กฐ๊ฑด์ ๋ฐ๋ผ null์ ๋ฐํํ์ฌ ์ปดํฌ๋ํธ๊ฐ ๋ ๋๋ง ๋์ง ์๋๋ก ํ ์ ์๋ค. ์๋์ ๊ฐ์ด ์กฐ๊ฑด๋ถ๋ก ์ปดํฌ๋ํธ๋ฅผ ๋ ๋๋งํ ์ ํ ์ ์๋ค.
function Greeting(props) { if (!props.loggedIn) { return null; } return ( <div className="greeting"> welcome, {props.name} </div> ); }
class User extends React.Component { constructor(props) { super(props); this.state = {loggedIn: false, name: 'John'}; } render() { return ( <div> //Prevent component render if it is not loggedIn <Greeting loggedIn={this.state.loggedIn} /> <UserDetails name={this.state.name}> </div> ); }
์์ ์์ ์์, greeting ์ปดํฌ๋ํธ๋ ์กฐ๊ฑด๋ฌธ์ ์ ์ฉํ๊ณ null ๊ฐ์ ๋ฐํํ์ฌ ๋ ๋๋ง ๋ถ๋ถ์ ๊ฑด๋๋ด๋ค.
-
index๋ฅผ ํค๋ก ์์ ํ๊ฒ ์ฌ์ฉํ๊ธฐ ์ํ 3๊ฐ์ง ์กฐ๊ฑด์ด ์๋ค.
- ๋ชฉ๋ก๊ณผ ํญ๋ชฉ์ ์ ์ ์ด๋ค. ๊ณ์ฐ๋์ง ์๊ณ ๋ณ๊ฒฝ ์์ ๊ฒฝ์ฐ.
- ๋ชฉ๋ก์ ์๋ ํญ๋ชฉ๋ค์ด id๊ฐ ์์ ๊ฒฝ์ฐ.
- ๋ชฉ๋ก์ด ๋ค์ ์ ๋ ฌ๋๊ฑฐ๋ ํํฐ๋ง ๋์ง ์์ ๊ฒฝ์ฐ
-
๋ฐฐ์ด ๋ด์์ ์ฌ์ฉ๋๋ ํค๋ ํ์ ์ฌ์ด์์ ๊ณ ์ ํด์ผ ํ์ง๋ง ์ ์ฒด์ ์ผ๋ก๋ ๊ณ ์ ํ ํ์ ์๋ค. ์ฆ, ๋ ๊ฐ์ ๋ค๋ฅธ ๋ฐฐ์ด์์ ๋์ผํ ํค๋ฅผ ์ฌ์ฉํ ์ ์๋ค. ์๋ฅผ ๋ค์ด, ์๋์ Book ์ปดํฌ๋ํธ๋ ๋ค๋ฅธ ๋ฐฐ์ด ๋ ๊ฐ๋ฅผ ์ฌ์ฉํ๊ณ ์๋ค.
function Book(props) { const index = ( <ul> {props.pages.map((page) => <li key={page.id}> {page.title} </li> )} </ul> ); const content = props.pages.map((page) => <div key={page.id}> <h3>{page.title}</h3> <p>{page.content}</p> <p>{page.pageNumber}</p> </div> ); return ( <div> {index} <hr /> {content} </div> ); }
-
Formik์ ์ ํจ์ฑ ๊ฒ์ฌ, ๋ฐฉ๋ฌธ ํ์ด์ง ์ถ์ , form ์ ์ถ ์ฒ๋ฆฌ์ ๊ฐ์ ์๋ฃจ์ ์ ์ ๊ณตํ๋ react ๋ผ์ด๋ธ๋ฌ๋ฆฌ๋ค. ๊ตฌ์ฒด์ ์ผ๋ก ๋ค๋ฆ๊ณผ ๊ฐ์ด ๋ถ๋ฅํ ์ ์๋ค.
- form state ๊ฐ ๊ฐ์ ธ์ค๊ธฐ
- ์ ํจ์ฑ ๊ฒ์ฌ์ ์ค๋ฅ ๋ฉ์์ง
- form ์ ์ถ ์ฒ๋ฆฌ
์ต์ํ์ API๋ก ํ์ฅํ ์ ์๊ณ ์ฑ๋ฅ์ด ๋ฐ์ด๋ form ํฌํผ๋ฅผ ์์ฑํ์ฌ ๊ท์ฐฎ์ ๊ฒ์ ํด๊ฒฐํ๋ ๋ฐ ์ฌ์ฉ๋๋ค.
-
๋ค์์ redux form ๋ผ์ด๋ธ๋ฌ๋ฆฌ๋ณด๋ค formik์ ๊ถ์ฅํ๋ ์ฃผ์ ์ด์ ์ด๋ค.
- form state๋ ๋ณธ์ง์ ์ผ๋ก ๋จ๊ธฐ์ ์ด๋ฉฐ ๋ก์ปฌ์ด๋ฏ๋ก Redux(๋๋ ๋ชจ๋ ์ข ๋ฅ์ Flux ๋ผ์ด๋ธ๋ฌ๋ฆฌ) ์์ ์ถ์ ํ ํ์๊ฐ ์๋ค.
- Redux-Form์ EVERY SINGLE KEYSTROKE์์ ์ ์ฒด ์ต์์ Redux reducer๋ฅผ ์ฌ๋ฌ ๋ฒ ํธ์ถํ๋ค. ์ด๋ ๊ฒ ํ๋ฉด ํฐ ์ฑ์ ์ ๋ ฅ ๋๊ธฐ ์๊ฐ์ด ๋์ด๋๋ค.
- Redux-Form์ 22.5 kB๋ก ์ถ์๋ gzip์ธ ๋ฐ๋ฉด Formik์ 12.7 kB์ด๋ค.
-
React์์ ์ปดํฌ๋ํธ ๊ฐ ์ฝ๋๋ฅผ ์ฌ์ฌ์ฉํ๋ ค๋ฉด ์์ ๋์ composition์ ์ฌ์ฉํ๋ ๊ฒ์ด ์ข๋ค. Props์ composition ๋ชจ๋ ๋ช ์์ ์ด๊ณ ์์ ํ ๋ฐฉ์์ผ๋ก ์ปดํฌ๋ํธ์ ํํ์ ๊ธฐ๋ฅ์ ์ปค์คํ ํ๋๋ฐ ํ์ํ ๋ชจ๋ ์ ์ฐ์ฑ์ ๋ณด์ฅํ๋ค. ๋ฐ๋ฉด, ์ปดํฌ๋ํธ ๊ฐ UI๊ฐ ์๋ ๊ธฐ๋ฅ์ ์ฌ์ฌ์ฉํ๋ ค๋ฉด, ๋ณ๋์ JavaScript ๋ชจ๋๋ก ๊ตฌ๋ถํ๋ ๊ฒ์ด ์ข๋ค. ์ดํ ์ปดํฌ๋ํธ๋ฅผ ๊ฐ์ ธ์ ํ์ฅํ์ง ์๊ณ ํจ์, ๊ฐ์ฒด, ํด๋์ค๋ฅผ ์ฌ์ฉํ๋ค.
-
react ์ ํ๋ฆฌ์ผ์ด์ ์์ ์น ์ปดํฌ๋ํธ๋ฅผ ์ฌ์ฉํ ์ ์๋ค. ๋ง์ ๊ฐ๋ฐ์๊ฐ ์ด ์กฐํฉ์ ์ฌ์ฉํ์ง ์๋๋ผ๋ ์น ์ปดํฌ๋ํธ๋ก ๋ง๋ค์ด์ง ๋ค๋ฅธ UI ์ปดํฌ๋ํธ๋ฅผ ์ฌ์ฉํ๋ ๊ฒฝ์ฐ ํนํ ํ์ํ ์ ์๋ค. ์๋ฅผ ๋ค์ด Vaadin ๋ ์ง ์ ํ๊ธฐ ์น ์ปดํฌ๋ํธ๋ฅผ ์ฌ์ฉํ๋ ๊ฒ์ ๋ณด์.
import React, { Component } from 'react'; import './App.css'; import '@vaadin/vaadin-date-picker'; class App extends Component { render() { return ( <div className="App"> <vaadin-date-picker label="When were you born?"></vaadin-date-picker> </div> ); } } export default App;
-
dynamic import() ๋ฌธ๋ฒ์ ํ์ฌ ํ์ค ๋ฌธ๋ฒ์ด ์๋ ECMAScript ์ ์์ด๋ค. ๊ฐ๊น์ด ๋ฏธ๋์ ์ถ๊ฐ๋ ๊ฒ์ด๋ค(์ค์ ๋ก 2020์ ์ถ๊ฐ ์์ ). dynamic import()๋ฅผ ์ฌ์ฉํ์ฌ code-splitting ํ ์ ์๋ค. ๋ง์ ์๋ฅผ ๋ค์ด๋ณด์.
- Normal Import
import { add } from './math'; console.log(add(10, 20));
- Dynamic Import
import("./math").then(math => { console.log(math.add(10, 20)); });
-
์๋ฒ ๋ ๋๋ง ์ฑ์์ code-splitting์ ํ๋ ๊ฒฝ์ฐ, React.lazy์ Suspense๋ฅผ ์์ง ์๋ฒ ์ธก ๋ ๋๋ง์ ์ฌ์ฉํ ์ ์์ผ๋ฏ๋ก Loadable ์ปดํฌ๋ํธ๋ฅผ ์ฌ์ฉํ๋ ๊ฒ์ด ์ข๋ค. Loadable์ ์ฌ์ฉํ๋ฉด dynamic import๋ฅผ ์ผ๋ฐ ์ปดํฌ๋ํธ๋ก ๋ ๋๋งํ ์ ์๋ค. ์๋ฅผ ๋ค์ด๋ณด๋ฉด,
import loadable from '@loadable/component' const OtherComponent = loadable(() => import('./OtherComponent')) function MyComponent() { return ( <div> <OtherComponent /> </div> ) }
์ด์ OtherComponent๋ ๋ณ๋์ ๋ฒ๋ค๋ก ๋ก๋๋๋ค.
-
dynamic import๊ฐ ํฌํจ๋ ๋ชจ๋์ ๋ถ๋ชจ ์ปดํฌ๋ํธ๊ฐ ๋ ๋๋ง ๋ ๋๊น์ง ๋ก๋๊ฐ ๋์ง ์์ ๊ฒฝ์ฐ ๋ก๋ฉ ํ์๊ธฐ๋ฅผ ์ฌ์ฉํ์ฌ ๋ก๋๋ ๋๊น์ง ๋์ฒด ์ปจํ ์ธ ๋ฅผ ํ์ํ๋ค. ์ด๋ฅผ Suspense ์ปดํฌ๋ํธ๋ฅผ ์ฌ์ฉํ์ฌ ์ํํ ์ ์๋ค. ์๋ฅผ ๋ค์ด ์๋ ์ฝ๋๋ suspense ์ปดํฌ๋ํธ๋ฅผ ์ฌ์ฉํ๋ค.
const OtherComponent = React.lazy(() => import('./OtherComponent')); function MyComponent() { return ( <div> <Suspense fallback={<div>Loading...</div>}> <OtherComponent /> </Suspense> </div> ); }
์ ์ฝ๋์์ ์ธ๊ธํ๋ฏ์ด Suspense๋ Layzy ์ปดํฌ๋ํธ๋ก ๊ฐ์ธ์ง๋ค.
-
์ฝ๋ ์คํ๋ฆฌํ ์ ์ฌ์ฉํด ๋ณผ ์ข์ ์ฅ์ ์ค ํ๋๋ ๋ผ์ฐํธ๋ค. ์ ์ฒด ํ์ด์ง๊ฐ ํ ๋ฒ์ ๋ ๋๋ง ๋๋ฏ๋ก ์ฌ์ฉ์๊ฐ ํ์ด์ง์ ๋ค๋ฅธ ์์์ ๋์์ ์ํธ ์์ฉํ ๊ฐ๋ฅ์ฑ์ด ์๋ค. ์ด๋ก ์ธํด ์ฌ์ฉ์ ๊ฒฝํ์ด ๋ฐฉํด๋ฐ์ง ์๋๋ค. React.lazy์ ํจ๊ป Reat Router ๊ฐ์ ๋ผ์ด๋ธ๋ฌ๋ฆฌ๋ฅผ ์ฌ์ฉํ์ฌ ๊ฒฝ๋ก ๊ธฐ๋ฐ ์น ์ฌ์ดํธ๋ฅผ ์๋ฅผ ๋ค์ด๋ณด์.
import { BrowserRouter as Router, Route, Switch } from 'react-router-dom'; import React, { Suspense, lazy } from 'react'; const Home = lazy(() => import('./routes/Home')); const About = lazy(() => import('./routes/About')); const App = () => ( <Router> <Suspense fallback={<div>Loading...</div>}> <Switch> <Route exact path="/" component={Home}/> <Route path="/about" component={About}/> </Switch> </Suspense> </Router> );
์์ ์ฝ๋์์, ์ฝ๋ ์คํ๋ฆฌํ ์ ๊ฐ ๊ฒฝ๋ก ์์ค์์ ๋ฐ์ํ๋ค.
-
Context๋ React ์ปดํฌ๋ํธ ํธ๋ฆฌ์์ global๋ก ๋ฐ์ดํฐ๋ฅผ ๊ณต์ ํ๋๋ก ์ค๊ณ๋์๋ค. ์๋ฅผ ๋ค์ด, ์๋ ์ฝ๋์์ Button ์ปดํฌ๋ํธ์ ์คํ์ผ์ ์ง์ ํ๊ธฐ ์ํด "ํ ๋ง" prop์ ์๋์ผ๋ก ์ฐ๊ฒฐํ ์ ์๋ค.
//Lets create a context with a default theme value "luna" const ThemeContext = React.createContext('luna'); // Create App component where it uses provider to pass theme value in the tree class App extends React.Component { render() { return ( <ThemeContext.Provider value="nova"> <Toolbar /> </ThemeContext.Provider> ); } } // A middle component where you don't need to pass theme prop anymore function Toolbar(props) { return ( <div> <ThemedButton /> </div> ); } // Lets read theme value in the button component to use class ThemedButton extends React.Component { static contextType = ThemeContext; render() { return <Button theme={this.context} />; } }
-
defaultValue ์ธ์๋ ์ปดํฌ๋ํธ์ ํธ๋ฆฌ์์ ์ผ์นํ๋ Provider๊ฐ ์๋ ๊ฒฝ์ฐ์ ์ฌ์ฉ๋๋ค. ์ด๋ ์ปดํฌ๋ํธ๋ฅผ ๊ฐ์ธ์ง ์๊ณ ๋ถ๋ฆฌํ์ฌ ํ ์คํธํ๋๋ฐ ๋์์ด ๋ ์ ์๋ค. ์๋ ์ฝ๋๋ ํ ๋ง ๊ธฐ๋ณธ๊ฐ์ Luna๋ก ์ ๊ณตํ๋ค.
const MyContext = React.createContext(defaultValue);
-
ContextType์ context ๊ฐ์ฒด๋ฅผ ์๋นํ๋๋ฐ ์ฌ์ฉ๋๋ค. contextType ์์ฑ์ 2๊ฐ์ง ๋ฐฉ์์ผ๋ก ์ฌ์ฉ๋๋ค.
- ํด๋์ค ์์ฑ์ผ๋ก contextType: ํด๋์ค์ contextType ์์ฑ์๋ React.createContext()๋ก ๋ง๋ Context ๊ฐ์ฒด๋ฅผ ํ ๋นํ ์ ์๋ค. ๊ทธ๋ฐ ๋ค์๋ชจ๋ ๋ผ์ดํ ์ฌ์ดํด ๋ฉ์๋์ ๋ ๋๋ง ํจ์์์ this.context๋ฅผ ์ฌ์ฉํ์ฌ ํด๋น contextType์ ๊ฐ์ฅ ๊ฐ๊น์ด ํ์ฌ ๊ฐ์ ์ฌ์ฉํ ์ ์๋ค. ์๋์ ๊ฐ์ด MyClass์ contextType ์์ฑ์ ํ ๋น ํ ์ ์๋ค.
class MyClass extends React.Component { componentDidMount() { let value = this.context; /* perform a side-effect at mount using the value of MyContext */ } componentDidUpdate() { let value = this.context; /* ... */ } componentWillUnmount() { let value = this.context; /* ... */ } render() { let value = this.context; /* render something based on the value of MyContext */ } } MyClass.contextType = MyContext;
- Static field ์ ์ ํด๋์ค ํ๋๋ฅผ ์ฌ์ฉํ์ฌ ๊ณต์ฉ ํด๋์ค ํ๋ ๊ตฌ๋ฌธ์ ์ฌ์ฉํ๋ contextType์ ์ด๊ธฐํํ ์ ์๋ค.
class MyClass extends React.Component { static contextType = MyContext; render() { let value = this.context; /* render something based on the value */ } }
-
Consumer๋ context ๋ณ๊ฒฝ์ ๊ตฌ๋ ํ๋ React ์ปดํฌ๋ํธ์ด๋ค. ํ์ฌ context ๊ฐ์ ์ธ์๋ก ๋ฐ๊ณ react node๋ฅผ ๋ฐํํ๋ ์์ ํจ์๊ฐ ํ์ํ๋ค. ํจ์์ ์ ๋ฌ๋ value ์ธ์๋ ํธ๋ฆฌ์์ context์์ ๊ฐ์ฅ ๊ฐ๊น์ด Provider์ prop ๊ฐ๊ณผ ๋์ผํ๋ค. ๊ฐ๋จํ ์๋ฅผ ๋ณด์.
<MyContext.Consumer> {value => /* render something based on the context value */} </MyContext.Consumer>
-
context๋ ๋ฆฌ๋ ๋๋งํ ์๊ธฐ๋ฅผ ๊ฒฐ์ ํ ๋ ์ฐธ์กฐ ID๋ฅผ ์ฌ์ฉํ๋ค. provider์ ๋ถ๋ชจ๊ฐ ๋ฆฌ๋ ๋๋ง ๋ ๋ consumer์์ ์๋ํ์ง ์์ ๋ ๋๋ง์ ์ผ์ผํฌ ์ ์๋ค. ์๋ฅผ ๋ค์ด, ์๋ ์ฝ๋๋ ์๋ก์ด ๊ฐ์ฒด๊ฐ ํญ์ ๋ง๋ค์ด์ง๋ฏ๋ก provider๊ฐ ๋ฆฌ๋ ๋๋ง ๋ ๋๋ง๋ค ๋ชจ๋ consumer๋ฅผ ๋ ๋๋งํ๋ค.
class App extends React.Component { render() { return ( <Provider value={{something: 'something'}}> <Toolbar /> </Provider> ); } }
์ด๋ ๊ฐ์ ์์ state๋ก ์ฌ๋ฆฌ๋ฉด ํด๊ฒฐํ ์ ์๋ค.
class App extends React.Component { constructor(props) { super(props); this.state = { value: {something: 'something'}, }; } render() { return ( <Provider value={this.state.value}> <Toolbar /> </Provider> ); } }
-
Refs๋ prop์ด ์๋๊ธฐ ๋๋ฌธ์ ๋๊ธฐ์ง ๋ชปํ๋ค. key์ ๋น์ทํ๊ฒ React์์๋ ๋ค๋ฅด๊ฒ ์ฒ๋ฆฌํ๋ค. HOC์ ref๋ฅผ ์ถ๊ฐํ๋ฉด ํด๋น ref๋ ๊ฐ์ธ์ง ์ปดํฌ๋ํธ๊ฐ ์๋ ๊ฐ์ฅ ๋ฐ๊นฅ์ชฝ ์ปจํ ์ด๋ ์ปดํฌ๋ํธ๋ฅผ ์ฐธ์กฐํ๋ค. ์ด ๊ฒฝ์ฐ, Forward Ref API๋ฅผ ์ฌ์ฉํ ์ ์๋ค. ์๋ฅผ ๋ค์ด, React.forwardRef API๋ฅผ ์ฌ์ฉํ์ฌ ๋ด๋ถ FancyButton ์ปดํฌ๋ํธ์ ref๋ฅผ ๋ช ์์ ์ผ๋ก ์ ๋ฌํ ์ ์๋ค.
์๋ HOC๋ ๋ชจ๋ props๋ฅผ ๊ธฐ๋กํ๋ค.
function logProps(Component) { class LogProps extends React.Component { componentDidUpdate(prevProps) { console.log('old props:', prevProps); console.log('new props:', this.props); } render() { const {forwardedRef, ...rest} = this.props; // Assign the custom prop "forwardedRef" as a ref return <Component ref={forwardedRef} {...rest} />; } } return React.forwardRef((props, ref) => { return <LogProps {...props} forwardedRef={ref} />; }); }
์ด HOC๋ฅผ ์ฌ์ฉํ์ฌ โfancy buttonโ ์ปดํฌ๋ํธ์ ์ ๋ฌ๋๋ ๋ชจ๋ props๋ฅผ ๊ธฐ๋กํด๋ณด์.
class FancyButton extends React.Component { focus() { // ... } // ... } export default logProps(FancyButton);
์ด์ ref๋ฅผ ๋ง๋ค์ด FancyButton ์ปดํฌ๋ํธ์ ์ ๋ฌํ๊ณ ์๋ค. ์ด ๊ฒฝ์ฐ, ๋ฒํผ ์๋ฆฌ๋จผํธ์ ํฌ์ปค์ค๋ฅผ ์ค์ ํ ์ ์๋ค.
import FancyButton from './FancyButton'; const ref = React.createRef(); ref.current.focus(); <FancyButton label="Click Me" handleClick={handleClick} ref={ref} />;
- ์ผ๋ฐ์ ์ธ ํจ์ ๋๋ ํด๋์ค ์ปดํฌ๋ํธ๋ ref ์ธ์๋ก ๋ฐ์ง ์์ผ๋ฉฐ, ref๋ props์์๋ ์ฌ์ฉํ ์ ์๋ค. ๋ ๋ฒ์งธ ref ์ธ์๋ React.forwardRef ํธ์ถ๋ก ์ปดํฌ๋ํธ๋ฅผ ์ ์ํ ๋๋ง ์กด์ฌํ๋ค.
- ์ปดํฌ๋ํธ ๋ผ์ด๋ธ๋ฌ๋ฆฌ์์ forwardRef๋ฅผ ์ฌ์ฉํ๊ธฐ ์์ํ๋ฉด, ์ฃผ์ ๋ณ๊ฒฝ์ฌํญ์ผ๋ก ์ทจ๊ธํ๊ณ ๋ผ์ด๋ธ๋ฌ๋ฆฌ์ ์ ์ฃผ์ ๋ฒ์ ์ ๋ฆด๋ฆฌ์ฆ ํด์ผ ํ๋ค. ๋ผ์ด๋ธ๋ฌ๋ฆฌ์ ref๊ฐ ์ง์ ๋๊ณ ๋ด๋ณด๋ด๋ ์ ํ๊ณผ ๊ฐ์ ๋ค๋ฅธ ๋์์ด ์์ ์ ์๋ค. ์ด๋ฌํ ๋ณ๊ฒฝ์ผ๋ก ์ธํด ์ด์ ๋์์ ์์กดํ๋ ์ฑ ๋ฐ ๊ธฐํ ๋ผ์ด๋ธ๋ฌ๋ฆฌ๊ฐ ์์๋ ์ ์๋ค.
-
ES6๋ฅผ ์ฌ์ฉํ์ง ์๋ ๋์ create-react-class ๋ชจ๋์ ์ฌ์ฉํด์ผ ํ ์ ์๋ค. ๊ธฐ๋ณธ props์ ๊ฒฝ์ฐ, ์ ๋ฌ๋ ๊ฐ์ฒด์ ํจ์๋ก getDefaultProps()๋ฅผ ์ ์ํด์ผ ํ๋ค. ์ด๊ธฐ state์ธ ๊ฒฝ์ฐ, ์ด๊ธฐ state๋ฅผ ๋ฐํํ๋ ๋ณ๋์ getInitialState ๋ฉ์๋๋ฅผ ์ ๊ณตํด์ผ ํ๋ค.
var Greeting = createReactClass({ getDefaultProps: function() { return { name: 'Jhohn' }; }, getInitialState: function() { return {message: this.props.message}; }, handleClick: function() { console.log(this.state.message); }, render: function() { return <h1>Hello, {this.props.name}</h1>; } });
Note: createReactClass๋ฅผ ์ฌ์ฉํ๋ฉด ๋ชจ๋ ๋ฉ์๋์ ์๋ ๋ฐ์ธ๋ฉ์ ์ฌ์ฉํ ์ ์๋ค. ์ฆ, ์ด๋ฒคํธ ํธ๋ค๋ฌ์ ์์ฑ์์ .bind(this)๋ฅผ ์ฌ์ฉํ ํ์๊ฐ ์๋ค.
-
๋ค, JSX๋ React๋ฅผ ์ฌ์ฉํ๋ ๋ฐ ํ์๊ฐ ์๋๋ค. ์ค์ ๋ก ๋น๋ ํ๊ฒฝ์์ ์ปดํ์ผ์ ์ค์ ํ์ง ์์ ๋ ํธ๋ฆฌํ๋ค. ๊ฐ JSX ์์๋ React.createElement(component, props, ...children)๋ฅผ ํธ์ถํ๊ธฐ ์ํ syntactic sugar์ด๋ค. ์๋ฅผ ๋ค์ด JSX๋ก ์ธ์ฌ(Greeting)๋ฅผ ๋ง๋ค์ด ๋ณด์.
class Greeting extends React.Component { render() { return <div>Hello {this.props.message}</div>; } } ReactDOM.render( <Greeting message="World" />, document.getElementById('root') );
์๋์ ๊ฐ์ด JSX ์์ด ๋์ผํ ์ฝ๋๋ฅผ ์์ฑํ ์ ์๋ค.
class Greeting extends React.Component { render() { return React.createElement('div', null, `Hello ${this.props.message}`); } } ReactDOM.render( React.createElement(Greeting, {message: 'World'}, null), document.getElementById('root') );
-
React๋ ์ต์ ํธ๋ฆฌ์ ์ผ์นํ๋๋ก UI๋ฅผ ํจ์จ์ ์ผ๋ก ์ ๋ฐ์ดํธํ๋ ๋ฐฉ๋ฒ์ ์ฐพ๊ธฐ ์ํด์ ์๊ณ ๋ฆฌ์ฆ์ ์ฌ์ฉํ๋ค. diffing ์๊ณ ๋ฆฌ์ฆ์ด๋ ํ๋์ ํธ๋ฆฌ๋ฅผ ๋ค๋ฅธ ํธ๋ฆฌ๋ก ๋ณํํ๊ธฐ ์ํด ์ต์ํ์ ์ ์ฐ์ฐ์ ํ๋ค. ๊ทธ๋ฌ๋, ์ด ์๊ณ ๋ฆฌ์ฆ์ O(n3)์ ๋ณต์ก์ฑ์ ๊ฐ์ง๋ฉฐ, ์ฌ๊ธฐ์ n์ ํธ๋ฆฌ์ element์ ์์ด๋ค. ์ด ๊ฒฝ์ฐ, 1000๊ฐ์ element๋ฅผ ํ์ํ๋ ค๋ฉด 10์ต ๊ฐ์ ๋น๊ต ์์๊ฐ ํ์ํ๋ค. ์ด๊ฒ์ ๋๋ฌด ๋น์ธ๋ค. ๋์ React๋ ๋ค์ ๋ ๊ฐ์ง ๊ฐ์ ์ ๊ธฐ๋ฐ์ผ๋ก ํด๋ฆฌ์คํฑ O(n) ์๊ณ ๋ฆฌ์ฆ์ ๊ตฌํํ๋ค.
- ๋ค๋ฅธ ์ ํ์ ๋ element๋ ๋ค๋ฅธ ํธ๋ฆฌ๋ฅผ ์์ฑํ ๊ฒ์ด๋ค.
- ๊ฐ๋ฐ์๋ key prop๋ฅผ ์ฌ์ฉํ์ฌ ๋ค๋ฅธ ๋ ๋๋ง์์ ์ด๋ค ํ์ element๊ฐ ๋ณ๊ฒฝ๋์ง ์๋์ง ์์ํ๋ค.
-
์๋ก ๋ค๋ฅธ ํธ๋ฆฌ๋ฅผ ๋น๊ตํ ๋, React๋ ๋จผ์ ๋ ๊ฐ์ root element๋ฅผ ๋น๊ตํ๋ค. root element์ ํ์ ์ ๋ฐ๋ผ ๋์์ด ๋ค๋ฅด๋ค. reconciliation ์๊ณ ๋ฆฌ์ฆ ์ค ์๋์ ๊ท์น์ ๋ฐ๋ฅธ๋ค.
- ๋ค๋ฅธ ํ์
์ Elements
root element๊ฐ ๋ค๋ฅธ ํ์
์ ๊ฐ์ง ๋๋ง๋ค React๋ ์ด์ ํธ๋ฆฌ๋ฅผ ๋ถํดํ๊ณ ์ฒ์๋ถํฐ ์๋ก์ด ํธ๋ฆฌ๋ฅผ ๋ง๋ ๋ค. ์๋ฅผ ๋ค์ด, elements
<a>
๋<img>
๋ก, ๋๋<Article>
์์<Comment>
๋ก ๋ค๋ฅธ ํ์ ์ element๋ ์ ์ฒด๋ฅผ ๋ค์ ์์ฑํ๋ค. - ๋์ผํ ํ์
์ DOM Elements
๋์ผํ ํ์
์ ๋ React DOM element๋ฅผ ๋น๊ตํ ๋ React๋ ๋ ์์ฑ์ ๋ชจ๋ ๋ณด๊ณ ๋์ผํ ๊ธฐ๋ณธ DOM ๋
ธ๋๋ฅผ ์ ์งํ๋ฉฐ ๋ณ๊ฒฝ๋ ์์ฑ๋ง ์
๋ฐ์ดํธํ๋ค. className ์์ฑ์ ์ ์ธํ ์์ฑ์ด ๋์ผํ DOM element๋ก ์์ ๋ฅผ ๋ณด์.
<div className="show" title="ReactJS" /> <div className="hide" title="ReactJS" />
- ๋์ผํ ํ์ ์ ์ปดํฌ๋ํธ Elements ์ปดํฌ๋ํธ๊ฐ ์ ๋ฐ์ดํธ ๋๋ฉด, ์ธ์คํด์ค๋ ๋์ผํ๊ฒ ์ ์ง๋๋ฏ๋ก ๋ ๋๋ง์์ state๋ ์ ์ง๋๋ค. React๋ ํ์ element ์ธ์คํด์ค์ prop์ ์ element์ ์ผ์นํ๋๋ก ์ ๋ฐ์ดํธํ๊ณ ํ์ ์ธ์คํด์ค์์ componentWillReceiveProps()์ componentWillUpdate()๋ฅผ ํธ์ถํ๋ค. ๊ทธ๋ฐ ๋ค์, render() ๋ฉ์๋๊ฐ ํธ์ถ๋๊ณ ์ด์ ๊ฒฐ๊ณผ์ ์ ๊ฒฐ๊ณผ์์ ์ฌ๊ท์ ์ผ๋ก diff ์๊ณ ๋ฆฌ์ฆ์ด ์ด๋ฃจ์ด์ง๋ค.
- Children ์ฌ๊ท
DOM ๋
ธ๋์ ์์์ ๋ํด ์ฌ๊ท๊ฐ ์ด๋ฃจ์ด์ง ๋, React๋ ๋ ๊ฐ์ ์์ ๋ชฉ๋ก์ ๋์์ ๋ฐ๋ณตํ๊ณ ์ฐจ์ด๊ฐ ์์ ๋๋ง๋ค ๋ณ์ด๋ฅผ ์ผ์ผํจ๋ค. ์๋ฅผ ๋ค์ด, ์์์ ๋์ element๋ฅผ ์ถ๊ฐํ ๋ ๋ ํธ๋ฆฌ๋ฅผ ๋ณ๊ฒฝ์ํค๋ ๊ฒ ์ ๋ฆฌํ๋ค.
<ul> <li>first</li> <li>second</li> </ul> <ul> <li>first</li> <li>second</li> <li>third</li> </ul>
- keys ํธ๋ค๋ง React๋ key ์์ฑ์ ์ง์ํ๋ค. ์์์ ํค๊ฐ ์์ผ๋ฉด, React๋ ์ด ํค๋ฅผ ์ฌ์ฉํ์ฌ ์๋ ํธ๋ฆฌ์ ์์์ ๋ค์ ํธ๋ฆฌ์ ์์๊ณผ ์ผ์น์ํจ๋ค. ์๋ฅผ ๋ค์ด, key๋ฅผ ์ถ๊ฐํ๋ฉด ํธ๋ฆฌ ๋ณํ์ด ํจ์จ์ ์ด๋ค.
<ul> <li key="2015">Duke</li> <li key="2016">Villanova</li> </ul> <ul> <li key="2014">Connecticut</li> <li key="2015">Duke</li> <li key="2016">Villanova</li> </ul>
- ๋ค๋ฅธ ํ์
์ Elements
root element๊ฐ ๋ค๋ฅธ ํ์
์ ๊ฐ์ง ๋๋ง๋ค React๋ ์ด์ ํธ๋ฆฌ๋ฅผ ๋ถํดํ๊ณ ์ฒ์๋ถํฐ ์๋ก์ด ํธ๋ฆฌ๋ฅผ ๋ง๋ ๋ค. ์๋ฅผ ๋ค์ด, elements
-
ref ์ฌ์ฉ ์ผ์ด์ค๋ ๊ฑฐ์ ์๋ค.
- ์ด์ , ํ ์คํธ ์ ํ, ๋ฏธ๋์ด ์ฌ์ ๊ด๋ฆฌ
- ๋ช ๋ นํ ์ ๋๋ฉ์ด์ ํธ๋ฆฌ๊ฑฐ
- ํ์ฌ DOM ๋ผ์ด๋ธ๋ฌ๋ฆฌ์ ํตํฉ
-
render props๋ผ ๋ถ๋ฆฌ๋ ํจํด์ด ์์ด๋, ์ด ํจํด์ ์ฌ์ฉํ๊ธฐ ์ํด render๋ผ ๋ถ๋ฆฌ๋ prop์ ์ฌ์ฉํ ํ์๋ ์๋ค. ์๋ฅผ ๋ค์ด, ์ฆ, ์ปดํฌ๋ํธ๊ฐ ๋ ๋๋ง ๋์์ ์๊ธฐ ์ํด ์ฌ์ฉ๋๋ ๊ธฐ๋ฅ์ธ ๋ชจ๋ prop์ ๊ธฐ์ ์ ์ผ๋ก โrender propโ์ด๋ค. render props์ ๋ํด ์์ prop์ ์๋ฅผ ๋ค์ด๋ณด์.
์ค์ ๋ก ์์ prop์ JSX element์ โ์์ฑโ ๋ชฉ๋ก์ ์ด๋ฆ์ ์ง์ ํ ํ์๊ฐ ์๋ค. ๋์ ์, element ๋ด๋ถ์ ์ง์ ์ ์งํ ์ ์๋ค.
<Mouse children={mouse => ( <p>The mouse position is {mouse.x}, {mouse.y}</p> )}/>
์์ ๊ธฐ์ ์ ์ฌ์ฉํ๋ ๋์(์ด๋ฆ ์์ด), ์์์ propTypes์ ํจ์์ฌ์ผ ํ๋ค๊ณ ๋ช ์์ ์ผ๋ก ์์ฑํด์ผ ํ๋ค.<Mouse> {mouse => ( <p>The mouse position is {mouse.x}, {mouse.y}</p> )} </Mouse>
Mouse.propTypes = { children: PropTypes.func.isRequired };
-
render ๋ฉ์๋ ์์์ ํจ์๋ฅผ ๋ง๋๋ ๊ฒฝ์ฐ, ์์ ์ปดํฌ๋ํธ์ ๋ชฉ์ ์ ๋ง์ง ์๋๋ค. ์์ prop ๋น๊ต๋ ํญ์ ์๋ก์ด props์ ๋ํด false๋ฅผ ๋ฐํํ๋ฉฐ ๊ฐ ๋ ๋๋ง์ ๋ ๋๋ง prop์ ๋ํด ์๋ก์ด ๊ฐ์ ์์ฑํ๋ค. ๋ ๋๋ง ํจ์๋ฅผ ์ธ์คํด์ค ๋ฉ์๋๋ก ์ ์ํ์ฌ ํด๊ฒฐํ ์ ์๋ค.
-
render prop๊ณผ ์ผ๋ฐ์ ์ธ ์ปดํฌ๋ํธ๋ฅผ ์ฌ์ฉํด์ higher-order components (HOC)๋ฅผ ๋ง๋ค ์ ์๋ค. ์๋ฅผ ๋ค์ด,
<Mouse>
์ปดํฌ๋ํธ ๋์ withMouse HOC๋ฅผ ์ฌ์ฉํ๋ ค๋ฉด render prop๊ณผ ํจ๊ป<Mouse>
๋ฅผ ์ฌ์ฉํ์ฌ ์ฝ๊ฒ ๋ง๋ค ์ ์๋ค.function withMouse(Component) { return class extends React.Component { render() { return ( <Mouse render={mouse => ( <Component {...this.props} mouse={mouse} /> )}/> ); } } }
์ด ๋ฐฉ๋ฒ์ผ๋ก render prop๋ ๋ ํจํด ์ค ํ๋๋ฅผ ์ฌ์ฉํ๋๋ก ํด์ค๋ค.
-
Windowing์ ์ฃผ์ด์ง ์๊ฐ์ ํ์ ์์ ๋ถ๋ถ ์งํฉ๋ง ๋ ๋๋งํ๋ ๊ธฐ์ ๋ก, ์ปดํฌ๋ํธ๋ฅผ ๋ค์ ๋ ๋๋งํ๋ ๋ฐ ๊ฑธ๋ฆฌ๋ ์๊ฐ๊ณผ ์์ฑ๋ DOM ๋ ธ๋์ ์๋ฅผ ํฌ๊ฒ ์ค์ผ ์ ์๋ค. ์ ํ๋ฆฌ์ผ์ด์ ์ด ๊ธด ๋ฐ์ดํฐ ๋ชฉ๋ก์ ๋ ๋๋งํ๋ ๊ฒฝ์ฐ ์ด ๊ธฐ์ ์ด ๊ถ์ฅ๋๋ค. react-window์ react-virtualized๋ ๋ชจ๋ ๋ชฉ๋ก, ๊ทธ๋ฆฌ๋ ๋ฐ ํ ์ด๋ธ ํ์ ๋ฐ์ดํฐ๋ฅผ ํ์ํ๊ธฐ ์ํด ์ฌ์ฌ์ฉ ๊ฐ๋ฅํ ์ฌ๋ฌ ์ปดํฌ๋ํธ๋ฅผ ์ ๊ณตํ๋ windowing ๋ผ์ด๋ธ๋ฌ๋ฆฌ์ด๋ค.
-
false, null, undefined ๋ฐ true ๊ฐ์ ํ์๊ฐ์ ์ ํจํ ์์์ด์ง๋ง, ์๋ฌด๊ฒ๋ ๋ ๋๋งํ์ง ์๋๋ค. ํ์ํ๋ ค๋ฉด ๋ฌธ์์ด๋ก ๋ณํํด์ผ ํ๋ค. ๋ฌธ์์ด๋ก ๋ณํํ๋ ๋ฐฉ๋ฒ์ ๋ํ ์์ ๋ฅผ ๋ณด์.
<div> My JavaScript variable is {String(myVariable)}. </div>
-
React portals๋ ์์ ์ปดํฌ๋ํธ๊ฐ overflow: hidden์ด๊ฑฐ๋ stacking context(z- ์์ธ, ์์น, ๋ถํฌ๋ช ๋ ๋ฑ ์คํ์ผ)์ ์ํฅ์ ์ฃผ๋ ํน์ฑ์ ์ํฅ์ ์ค๋ค๋ฉด, ์ปจํ ์ด๋์์ ์๊ฐ์ ์ผ๋ก "๋ถ๋ฆฌ"ํ๋๋ฐ ๋์์ ์ค๋ค. ์๋ฅผ ๋ค์ด, dialogs, global message notifications, hovercards, ํดํ์ด ์๋ค.
-
React์์, form ์์์์ value ์์ฑ์ DOM์ ๊ฐ์ ๋์ฒดํ๋ค. ์ ์ด๋์ง ์๋ ์ปดํฌ๋ํธ๋ฅผ ์ฌ์ฉํ๋ฉด React๊ฐ ์ด๊ธฐ๊ฐ์ ์ง์ ํ์ง๋ง, ์ ๋ฐ์ดํธ๋ ์ ์ด๋์ง ์๋ ์ํ๋ก ๋๊ฒ ๋๋ค. ์ด ๊ฒฝ์ฐ๋ฅผ ์ฒ๋ฆฌํ๊ธฐ ์ํด value ๋์ defaultValue ์์ฑ์ ์ง์ ํ ์ ์๋ค.
render() { return ( <form onSubmit={this.handleSubmit}> <label> User Name: <input defaultValue="John" type="text" ref={this.input} /> </label> <input type="submit" value="Submit" /> </form> ); }
select
๊ณผtextArea
์ ๋ ฅ์๋ ๋์ผํ๊ฒ ์ ์ฉ๋๋ค. ๊ทธ๋ฌ๋checkbox
๋ฐradio
์ ๋ ฅ์๋ defaultChecked ๋ฅผ ์ฌ์ฉํด์ผ ํ๋ค.
-
๊ธฐ์ ์คํ์ ๊ฐ๋ฐ์๋ง๋ค ๋ค๋ฅด์ง๋ง ๊ฐ์ฅ ์ธ๊ธฐ ์๋ ์คํ์ react ๋ณด์ผ๋ฌ ํ๋ ์ดํธ ํ๋ก์ ํธ ์ฝ๋์ ์ฌ์ฉ๋๋ค. ์ฃผ๋ก ์ํ ๊ด๋ฆฌ ๋ฐ ๋น๋๊ธฐ ๋ถ์์ฉ์ ์ํด Redux ๋ฐ redux-saga, ๋ผ์ฐํ ๋ชฉ์ ์ ์ํ react-router, react ์ปดํฌ๋ํธ๋ฅผ ์คํ์ผ๋งํ๊ธฐ ์ํ styled-component, REST api๋ฅผ ํธ์ถํ๊ธฐ ์ํ axios, ๊ทธ๋ฆฌ๊ณ ๋ค๋ฅธ ์คํ๋ค๋ก๋ webpack, reselect, ESNext, Babel์ด ์๋ค. https://github.com/react-boilerplate/react-boilerplate๋ฅผ clone ๋ฐ์์ ์๋ก์ด react ํ๋ก์ ํธ ์์ ์ ์์ํ ์ ์๋ค.
-
์๋์ Real DOM๊ณผ Virtual DOM์ ์ฃผ์ ์ฐจ์ด์ ์ ์ดํด๋ณด์,
Real DOM Virtual DOM ์ ๋ฐ์ดํธ๊ฐ ๋๋ฆฌ๋ค ์ ๋ฐ์ดํธ๊ฐ ๋น ๋ฅด๋ค DOM ์กฐ์์ด ์ ๋ ดํ๋ค DOM ์กฐ์์ด ๋งค์ฐ ์ฝ๋ค. HTML์ ์ง์ ์ ๋ฐ์ดํธํ๋ค. HTML์ ์ง์ ์ ๋ฐ์ดํธ ํ ์ ์๋ค. ๋๋ฌด ๋ง์ ๋ฉ๋ชจ๋ฆฌ ๋ญ๋น๊ฐ ๋ฐ์ํ๋ค. ๋ฉ๋ชจ๋ฆฌ ๋ญ๋น๊ฐ ์๋ค. element๊ฐ ์ ๋ฐ์ดํธ ๋๋ฉด ์ DOM์ ๋ง๋ ๋ค. element๊ฐ ์ ๋ฐ์ดํธ ๋๋ฉด JSX๋ฅผ ์ ๋ฐ์ดํธํ๋ค.
-
๋ถํธ์คํธ๋ฉ์ ์ธ ๊ฐ์ง ๋ฐฉ๋ฒ์ผ๋ก React ์ฑ์ ์ถ๊ฐํ ์ ์๋ค.
-
๋ถํธ์คํธ๋ฉ CDN ์ฌ์ฉ: ๋ถํธ์คํธ๋ฉ์ ์ถ๊ฐํ๋ ๊ฐ์ฅ ์ฌ์ด ๋ฐฉ๋ฒ์ด๋ค. ๋ถํธ์คํธ๋ฉ CSS ๋ฐ JS ์์์ ํค๋ ํ๊ทธ์ ์ถ๊ฐํ๋ค.
-
๋ถํธ์คํธ๋ฉ Dependency ์ถ๊ฐํ๊ธฐ: ๋น๋ ๋๊ตฌ๋ Webpack๊ณผ ๊ฐ์ ๋ชจ๋ ๋ฒ๋ค๋ฌ๋ฅผ ์ฌ์ฉํ๋ ๊ฒฝ์ฐ React ์ ํ๋ฆฌ์ผ์ด์ ์ ๋ถํธ์คํธ๋ฉ์ ์ถ๊ฐํ๋ ๊ฒ ์ข๋ค.
npm install bootstrap ``
-
React ๋ถํธ์คํธ๋ฉ ํจํค์ง : ์ด ๊ฒฝ์ฐ, ๋ถํธ์คํธ๋ฉ ์ปดํฌ๋ํธ๋ฅผ ์ฌ๋น๋ํ ํจํค์ง๋ฅผ ์ฌ์ฉํ์ฌ ํนํ React ์ปดํฌ๋ํธ๋ก ์๋ํ๋ React ์ฑ์ ๋ถํธ์คํธ๋ฉ์ ์ถ๊ฐํ ์ ์๋ค. ์๋ ํจํค์ง๋ ์ธ๊ธฐ์๋ ํจํค์ง ์ค ํ๋์ด๋ค.
- react-bootstrap
- reactstrap
-
-
ํ๋ก ํธ์๋ ํ๋ ์์ํฌ๋ก React๋ฅผ ์ฌ์ฉํ๋ ๋ฉ์ธ ์น์ฌ์ดํธ๋ ์ ํ๋ฆฌ์ผ์ด์ ์?
์๋๋ React๋ฅผ ํ๋ก ํธ์๋ ํ๋ ์์ํฌ๋ก ์ฌ์ฉํ๋
top 10 websites
์ด๋ค.- Uber
- Khan Academy
- Airbnb
- Dropbox
- Netflix
- PayPal
-
React๋ ์คํ์ผ์ ์ด๋ป๊ฒ ํ๋ผ๋ ์ ์๊ฐ ์์ด์, ์ด๋ณด์๋ผ๋ฉด ์คํ์ผ์ ํ์์ ๊ฐ์ด ๋ณ๋์ *.css ํ์ผ์ ์ ์ํ๊ณ className์ ์ฌ์ฉํ์ฌ ์ฐธ์กฐํ๋ ๊ฒ์ด ์ข๋ค. CSS In JS ๊ธฐ๋ฅ์ React์ ์ผ๋ถ๋ ์๋์ง๋ง ํ์ฌ ๋ผ์ด๋ธ๋ฌ๋ฆฌ์์ ์ ๊ณตํ๋ ๊ฒ์ด๋ค. ๊ทธ๋ฌ๋ ๋ค๋ฅธ ์ ๊ทผ๋ฒ(CSS-In-JS)์ ์๋ํ๋ ค๋ฉด ์คํ์ผ ์ปดํฌ๋ํธ ๋ผ์ด๋ธ๋ฌ๋ฆฌ๊ฐ ์ข์ ๋ฐฉ์์ด๋ค.
-
์๋์. ๊ทธ๋ฌ๋ ๊ธฐ์กด ์ฝ๋๋ฅผ ๋ค์ ์์ฑํ์ง ์๊ณ ๋ ์ผ๋ถ ์ปดํฌ๋ํธ(๋๋ ์๋ก์ด ์ปดํฌ๋ํธ)์์ hook์ ์๋ํ ์ ์๋ค. ReactJS์์ ํด๋์ค๋ฅผ ์ ๊ฑฐํ ๊ณํ์ด ์๊ธฐ ๋๋ฌธ์ด๋ค.
-
useEffect
๋ผ๋ effect hook์ API์์ axios๋ฅผ ์ฌ์ฉํ์ฌ ๋ฐ์ดํฐ๋ฅผ ๊ฐ์ ธ์ค๊ณ state hook์ ์ ๋ฐ์ดํธ ๊ธฐ๋ฅ์ ์ฌ์ฉํ์ฌ ์ปดํฌ๋ํธ์ ๋ก์ปฌ ์ํ๋ก ๋ฐ์ดํฐ๋ฅผ ์ค์ ํ๋ ๋ฐ ์ฌ์ฉ๋๋ค. API๋ก ๊ธฐ์ฌ ๋ชฉ๋ก์ ๊ฐ์ ธ์ค๋ ์๋ฅผ ๋ค์ด๋ณด์.import React, { useState, useEffect } from 'react'; import axios from 'axios'; function App() { const [data, setData] = useState({ hits: [] }); useEffect(async () => { const result = await axios( 'http://hn.algolia.com/api/v1/search?query=react', ); setData(result.data); }, []); return ( <ul> {data.hits.map(item => ( <li key={item.objectID}> <a href={item.url}>{item.title}</a> </li> ))} </ul> ); } export default App;
์ปดํฌ๋ํธ ์ ๋ฐ์ดํธ ์ ์๋ํ์ง ์๊ณ ์ปดํฌ๋ํธ๋ฅผ ๋ง์ดํธํ ๋๋ง ์๋ํ๊ฒ ํ๊ธฐ ์ํด effect hook์ ๋ ๋ฒ์งธ ์ธ์๋ก ๋น ๋ฐฐ์ด์ ์ ๊ณตํ๋ค. ์ฆ ์ปดํฌ๋ํธ ๋ง์ดํธ์ ๋ํด์๋ง ๊ฐ์ ธ์จ๋ค.
-
Hook์ ํด๋์ค์ ๋ชจ๋ ์ฌ์ฉ ์ฌ๋ก๋ฅผ ๋ค๋ฃจ์ง๋ ๋ชปํ์ง๋ง, ๊ณง ์ถ๊ฐํ ๊ณํ์ด ์๋ค. ํ์ฌ๋ ์ผ๋ฐ์ ์ด์ง ์์ getSnapshotBeforeUpdate์ componentDidCatch ๋ผ์ดํ ์ฌ์ดํด์ ํด๋นํ๋ Hook์ด ์๋ค.
-
React๋ 16.8 ๋ฆด๋ฆฌ์ฆ์ ์์ ์ ์ธ ํจํค์ง๋ก ์๋ ํจํค์ง๋ฅผ ์์ ์ ์ผ๋ก ๊ตฌํํ์๋ค.
- React DOM
- React DOM Server
- React Test Renderer
- React Shallow Renderer
-
useState
๋ก state ๋ณ์๋ฅผ ์ ์ธํ๋ฉด ๋ ํญ๋ชฉ์ด ์๋ ๋ฐฐ์ด์ธ ์์ ๋ฐํํ๋ค. ์ฒซ ๋ฒ์งธ ํญ๋ชฉ์ ํ์ฌ ๊ฐ์ด๊ณ , ๋ ๋ฒ์งธ ํญ๋ชฉ์ ๊ฐ์ ์ ๋ฐ์ดํธํ๋ ํจ์์ด๋ค. [0]๊ณผ [1]์ ์ฌ์ฉํ์ฌ ์ก์ธ์คํ๋ ๊ฒ์ ํน์ ํ ์๋ฏธ๋ฅผ ๊ฐ์ง๊ธฐ ๋๋ฌธ์ ์ฝ๊ฐ ํผ๋๋๋ค. ์ฐ๋ฆฌ๊ฐ ๋์ destructuring์ ์ฌ์ฉํ๋ ์ด์ ์ด๋ค.์๋ฅผ ๋ค์ด, ๋ฐฐ์ด ์ธ๋ฑ์ค ์ก์ธ์ค๋ ๋ค์๊ณผ ๊ฐ๋ค.
var userStateVariable = useState('userProfile'); // Returns an array pair var user = userStateVariable[0]; // Access first item var setUser = userStateVariable[1]; // Access second item
๋ฐฐ์ด destructuring์ ์ฌ์ฉํ๋ฉด ๋ค์๊ณผ ๊ฐ์ด ์ก์ธ์คํ ์ ์๋ค.
const [user, setUser] = useState('userProfile');
-
Hooks๋ ์ฌ๋ฌ ๋ค๋ฅธ ์ถ์ฒ์์ ์์ด๋์ด๋ฅผ ์ป์๋ค. ์๋๋ ๊ทธ์ค ์ผ๋ถ์ด๋ค.
- react-future repository์์ ํจ์ํ API๋ฅผ ์ฌ์ฉํ ์ด์ ์ ์คํ.
- Reactions Component์ ๊ฐ์ ๋ ๋๋ง propAPI๋ฅผ ์ฌ์ฉํ ์ปค๋ฎค๋ํฐ ์คํ.
- DisplayScript์ state ๋ณ์ ๋ฐ state ์ .
- Rx์ ๊ตฌ๋ .
- ReasonReact์ Reducer ์ปดํฌ๋ํธ.
-
Web ์ปดํฌ๋ํธ๋ ์ข ์ข ๊ธฐ๋ฅ์ ๊ตฌํํ๊ธฐ ์ํด ๋ช ๋ นํ API๋ฅผ ๋ ธ์ถํ๋ค. ์น ์ปดํฌ๋ํธ์ ๋ช ๋ นํ API์ ์ก์ธ์คํ๋ ค๋ฉด ref๋ฅผ ์ฌ์ฉํ์ฌ DOM ๋ ธ๋์ ์ง์ ์ํธ ์์ฉํด์ผ ํ๋ค. ๊ทธ๋ฌ๋ ํ์ฌ ์น ์ปดํฌ๋ํธ๋ฅผ ์ฌ์ฉํ๋ ๊ฒฝ์ฐ ๊ฐ์ฅ ์ข์ ํด๊ฒฐ์ฑ ์ ์น ์ปดํฌ๋ํธ์ wrapper๋ก ๋์ํ๋ React ์ปดํฌ๋ํธ๋ฅผ ์์ฑํ๋ ๊ฒ์ด๋ค.
-
Formik๋ ์ธ ๊ฐ์ง ์ฃผ์ ๋ฌธ์ ๋ฅผ ํด๊ฒฐํ๋ ๋ฐ ๋์์ด ๋๋ ์์ react ๋ผ์ด๋ธ๋ฌ๋ฆฌ์ด๋ค.
- form state์ ๊ฐ ๊ฐ์ ธ์ค๊ธฐ
- ์ ํจ์ฑ ๊ฒ์ฌ์ ์ค๋ฅ ๋ฉ์์ง
- form ์ ์ถ ์ฒ๋ฆฌ
-
Redux eco system์์ ๋น๋๊ธฐ์ ํธ์ถ์ ์ฒ๋ฆฌํ๊ธฐ ์ํด ๋ง์ด ์ฌ์ฉ๋๋ ๋ฏธ๋ค์จ์ด๋
Redux Thunk, Redux Promise, Redux Saga
์ด๋ค.
-
์๋์, ๋ธ๋ผ์ฐ์ ๋ JSX ์ฝ๋๋ฅผ ์ดํดํ ์ ์๋ค. ๋ธ๋ผ์ฐ์ ๊ฐ ์ดํดํ ์ ์๋ JSX๋ฅผ ์ผ๋ฐ Javascript๋ก ๋ณํํ๋ ค๋ฉด ํธ๋์คํ์ผ๋ฌ๊ฐ ํ์ํ๋ค. ํ์ฌ ๊ฐ์ฅ ๋๋ฆฌ ์ฌ์ฉ๋๋ ํธ๋์คํ์ผ๋ฌ๋ Babel์ด๋ค.
-
React๋ ๋ณด์ผ๋ฌ ํ๋ ์ดํธ๋ฅผ ์ค์ด๊ณ ๊ธฐ์กด์ ์๋ฐฉํฅ ๋ฐ์ดํฐ ๋ฐ์ธ๋ฉ๋ณด๋ค ์ดํดํ๊ธฐ ์ฝ๋๋ก props๋ฅผ ์ฌ์ฉํ์ฌ ๋จ๋ฐฉํฅ ๋ฐ์์ฑ ๋ฐ์ดํฐ ํ๋ฆ์ ๊ตฌํํ์๋ค.
-
react-scripts
ํจํค์ง๋ create-react-app starter pack์ ์ธํธ๋ก ๋ณ๋์ ์ค์ ์ ํ์ง ์๊ณ ํ๋ก์ ํธ๋ฅผ ์์ํ ์ ์๋ค.react-scripts start
๋ช ๋ น์ด๋ ๊ฐ๋ฐ ํ๊ฒฝ์ ์ค์ ํ๊ณ ์๋ฒ์ ํซ ๋ชจ๋ ๋ฆฌ๋ก๋ฉ์ ์์ํ๋ค.
-
์๋๋ create react app์์ ์ ๊ณตํ๋ ์ผ๋ถ ๊ธฐ๋ฅ ๋ชฉ๋ก์ด๋ค.
- React, JSX, ES6, Typescript์ Flow syntax ์ง์.
- Autoprefixed CSS
- CSS Reset/Normalize
- ๋ผ์ด๋ธ ๊ฐ๋ฐ ์๋ฒ
- ์ปค๋ฒ๋ฆฌ์ง ๋ฆฌํฌํ ์ ์ง์ํ๋ ๋น ๋ฅธ interactive unit ํ ์คํธ runner ๋ด์ฅ
- hashes์ sourcemaps๋ฅผ ํฌํจํ production์ ์ํ JS, CSS, ์ด๋ฏธ์ง ๋ฒ๋ค ๋น๋ ์คํฌ๋ฆฝํธ ์ ๊ณต
- ๋ชจ๋ Progressive Web App ๊ธฐ์ค์ ์ถฉ์กฑํ๋ ์คํ๋ผ์ธ ์ฐ์ ์๋น์ค ์์ปค์ ์น ์ฑ manifest
-
ReactDOMServer#renderToNodeStream
๋ฉ์๋๋ ์๋ฒ์์ ์ด๊ธฐ ์์ฒญ์ ๋ ๋น ๋ฅธ ํ์ด์ง ๋ก๋๋ฅผ ์ํด HTML์ ์์ฑํ๋ ๋ฐ ์ฌ์ฉํ๋ค. ๋ํ, ๊ฒ์์์ง์ด SEO ๋ชฉ์ ์ผ๋ก ํ์ด์ง๋ฅผ ์ฝ๊ฒ ํฌ๋กค๋งํ๋ ๋ฐ ๋์์ ์ค๋ค. ์ฐธ๊ณ : ์ด ๋ฐฉ๋ฒ์ ๋ธ๋ผ์ฐ์ ์์๋ ์ฌ์ฉํ ์ ์์ผ๋ฉฐ ์๋ฒ์์๋ง ์ฌ์ฉ ๊ฐ๋ฅํ๋ค.
-
MobX๋ functional reactive programming (TFRP)์ ์ ์ฉํ๊ธฐ ์ํ ๋จ์ํ๋ฉฐ ํ์ฅ์ฑ์ด ์ข๊ณ battle tested ์ํ ๊ด๋ฆฌ ์๋ฃจ์ ์ด๋ค. reactJs ์ ํ๋ฆฌ์ผ์ด์ ์ ๊ฒฝ์ฐ, ์๋ ํจํค์ง๋ฅผ ์ค์นํด์ผ ํ๋ค.
npm install mobx --save npm install mobx-react --save
-
๋ค์์ Redux์ MobX์ ์ฃผ์ ์ฐจ์ด์ ์ด๋ค.
Topic Redux MobX ์ ์ ์ ํ๋ฆฌ์ผ์ด์ ์ํ๋ฅผ ๊ด๋ฆฌํ๊ธฐ ์ํ ์๋ฐ์คํฌ๋ฆฝํธ ๋ผ์ด๋ธ๋ฌ๋ฆฌ ์ ํ๋ฆฌ์ผ์ด์ ์ํ๋ฅผ ๋ฐ์์ ์ผ๋ก ๊ด๋ฆฌํ๊ธฐ ์ํ ๋ผ์ด๋ธ๋ฌ๋ฆฌ ํ๋ก๊ทธ๋๋ฐ ์ฃผ๋ก ES6 ์ฃผ๋ก JavaScript(ES5) ๋ฐ์ดํฐ ์ ์ฅ ๋ฐ์ดํฐ ์ ์ฅ์ ์ํ ํ๋์ ํฐ ์ ์ฅ์๊ฐ ์กด์ฌ ์ ์ฅ์ ์ํ ๋ ๊ฐ ์ด์์ ์คํ ์ด๊ฐ ์กด์ฌ ์ฌ์ฉ์ฑ ์ฃผ๋ก ํฌ๊ณ ๋ณต์กํ ์์ฉ ๋ถ์ผ์ ์ฌ์ฉ ๋จ์ํ ์ ํ๋ฆฌ์ผ์ด์ ์ ์ฌ์ฉ ์ฑ๋ฅ ๊ฐ์ ์ด ํ์ ๋ ๋์ ์ฑ๋ฅ ์ ๊ณต ์ ์ฅ๋ฐฉ์ JS Object๋ฅผ ์ฌ์ฉํ์ฌ ์ ์ฅ observable๋ฅผ ์ฌ์ฉํ์ฌ ๋ฐ์ดํฐ ์ ์ฅ
-
์๋์, react๋ฅผ ๋ฐฐ์ฐ๊ธฐ ์ํด์ es2015/es6๋ฅผ ๋ฐฐ์ธ ํ์๋ ์๋ค. ๊ทธ๋ฌ๋ ๋ง์ ๋ฆฌ์์ค์ React ์ํ๊ณ์์ ES6๋ฅผ ๊ด๋ฒ์ํ๊ฒ ์ฌ์ฉํ๋ค. ์์ฃผ ์ฌ์ฉ๋๋ ES6 ๊ธฐ๋ฅ ์ค ์ผ๋ถ๋ฅผ ๋ณด๋๋ก ํ์.
- Destructuring: props๋ฅผ ๊ฐ์ ธ์์ ์ปดํฌ๋ํธ์์ ์ฌ์ฉํ๋ค.
// in es 5 var someData = this.props.someData var dispatch = this.props.dispatch // in es6 const { someData, dispatch } = this.props
- Spread operator: props๋ฅผ ์ปดํฌ๋ํธ์ ์ ๋ฌํ๋๋ฐ ๋์์ด ๋๋ค.
// in es 5 <SomeComponent someData={this.props.someData} dispatch={this.props.dispatch} /> // in es6 <SomeComponent {...this.props} />
- Arrow functions: ๊ฐ๊ฒฐํ ๊ตฌ๋ฌธ์ ๋ง๋ค์ด ์ค๋ค.
// es 5 var users = usersList.map(function (user) { return <li>{user.name}</li> }) // es 6 const users = usersList.map(user => <li>{user.name}</li>);
-
Concurrent rendering์ ๊ธฐ๋ณธ UI ์ค๋ ๋๋ฅผ ์ฐจ๋จํ์ง ์๊ณ ์ปดํฌ๋ํธ ํธ๋ฆฌ๋ฅผ ๋ ๋๋งํ์ฌ React ์ฑ์ ์๋ต์ฑ์ ํฅ์ํ๋ค. React๊ฐ ์ฐ์ ์์๊ฐ ๋์ ์ด๋ฒคํธ๋ฅผ ์ฒ๋ฆฌํ๊ธฐ ์ํด ์ฅ์๊ฐ ๊ฑธ๋ฆฌ๋ ๋ ๋๋ง์ ์ค๋จํ ์ ์๋ค. ์ฆ, ๋์ ๋ชจ๋๋ฅผ ํ์ฑํ ํ๋ฉด React๋ ์ํํด์ผ ํ๋ ๋ค๋ฅธ ์์ ์ ์ฃผ์ํ๊ณ ์ฐ์ ์์๊ฐ ๋์ ๊ฒ์ด ์์ผ๋ฉด ํ์ฌ ๋ ๋๋ง ์ค์ธ ๊ฒ์ ์ผ์ ์ค์งํ๊ณ ๋ค๋ฅธ ์์ ์ ๋จผ์ ์๋ฃํ๋ค. ์ด๋ฅผ ๋ ๊ฐ์ง ๋ฐฉ๋ฒ์ผ๋ก ํ์ฑํ ํ ์ ์๋ค.
// 1. Part of an app by wrapping with ConcurrentMode <React.unstable_ConcurrentMode> <Something /> </React.unstable_ConcurrentMode> // 2. Whole app using createRoot ReactDOM.unstable_createRoot(domNode).render(<App />);
-
๋ ๋ค ๊ฐ์ ๊ฒ์ ๋ปํ๋ค. ์ด์ ์ ๋์ ๋ชจ๋๋ React ํ์์ "๋น๋๊ธฐ ๋ชจ๋"๋ผ๊ณ ํ๋ค. ์ด๋ฆ์ด ๋ค๋ฅธ ์ฐ์ ์์ ์์ค์์ ์์ ์ ์ํํ๋ React์ ๊ธฐ๋ฅ์ ๊ฐ์กฐํ๋๋ก ๋ณ๊ฒฝ๋์๋ค. ๋ฐ๋ผ์ ๋น๋๊ธฐ ๋ ๋๋ง์ ๋ํ ๋ค๋ฅธ ์ ๊ทผ ๋ฐฉ์์ ํผ๋์ ํผํ๋ค.
-
๋ค, javascript: URL์ ์ฌ์ฉํ ์ ์์ง๋ง, ์ฝ์์ ๊ฒฝ๊ณ ๋ฅผ ๊ธฐ๋กํ๋ค.
javascript:
๋ก ์์ํ๋ URL์<a href>
์ ๊ฐ์ ํ๊ทธ์ ์ข์ง ์์ ์ถ๋ ฅ์ ํฌํจํ์ฌ ๋ณด์ ํ์ ์ ๋ง๋ค์ด ์ํํ๋ค.const companyProfile = { website: "javascript: alert('Your website is hacked')", }; // It will log a warning <a href={companyProfile.website}>More details</a>
ํฅํ ๋ฒ์ ์์๋ ์๋ฐ์คํฌ๋ฆฝํธ URL ์ค๋ฅ๊ฐ ๋ฐ์ํ ๊ฒ์ด๋ค.
-
ESLint ํ๋ฌ๊ทธ์ธ์ ๋ฒ๊ทธ๋ฅผ ํผํ๊ณ ์ Hooks์ ๊ท์น์ ๊ฐ์กฐํ๋ค. ๋ชจ๋ ํจ์ โuseโ๋ก ์์ํ๋ฉฐ Hook ๋ฐ๋ก ๋ค์ ๋๋ฌธ์๊ฐ ์์ด์ผ ํ๋ค.
- Hooks์ ํธ์ถ์ PascalCase ํจ์ ๋ด(์ปดํฌ๋ํธ๋ก ๊ฐ์ ) ๋๋ ๋ค๋ฅธ useSomething ํจ์(์ปค์คํ Hook์ผ๋ก ๊ฐ์ ) ๋ด์ ์๋ค.
- Hooks๋ ๋ชจ๋ ๋ ๋๋ง์์ ๋์ผํ ์์๋ก ํธ์ถ๋๋ค.
-
"์ข์์" ๋ฒํผ๊ณผ ๊ฐ์ ๊ฐ๋จํ UI ์ปดํฌ๋ํธ๋ฅผ ์์ํด๋ณด์. ํญ ํ๋ฉด ์ด์ ์ ํ์์ด๋ ๊ฒ์ด ํ๋์์ผ๋ก ๋ฐ๋๊ณ ํ๋์์ด์์ผ๋ฉด ํ์์ผ๋ก ๋ฐ๋๋ค.
์ด๋ฅผ ์ํํ๋ ๋ช ๋ น์ ๋ฐฉ๋ฒ์ ์๋์ ๊ฐ๋ค.
if( user.likes() ) { if( hasBlue() ) { removeBlue(); addGrey(); } else { removeGrey(); addBlue(); } }
๊ธฐ๋ณธ์ ์ผ๋ก ํ์ฌ ํ๋ฉด์ ๋ด์ฉ์ ํ์ธํ๊ณ ์ด์ ์ํ์์ ๋ณ๊ฒฝ๋๋ ๋ด์ฉ์ ์คํ ์ทจ์ํ๋ ๋ฑ ํ์ฌ ์ํ๋ก ๋ค์ ๊ทธ๋ฆฌ๋ ๋ฐ ํ์ํ ๋ชจ๋ ๋ณ๊ฒฝ์ฌํญ์ ์ฒ๋ฆฌํด์ผ ํ๋ค. ์ค์ ์๋๋ฆฌ์ค์์ ์ด๊ฒ์ด ์ผ๋ง๋ ๋ณต์กํ์ง ์์ํ ์ ์๋ค.
๋ฐ๋๋ก, ์ ์ธ์ ์ ๊ทผ ๋ฐฉ์์ ์๋์ ๊ฐ๋ค.
if( this.state.liked ) { return <blueLike />; } else { return <greyLike />; }
์ ์ธ์ ์ ๊ทผ ๋ฐฉ์์ ์ฐ๋ ค๋๋ ์ฌํญ์ ๊ตฌ๋ถํ๊ธฐ ๋๋ฌธ์ ์ด ๋ถ๋ถ์ UI๊ฐ ๋ณ๊ฐ์ ์ํ๋ก ํ์๋๋ ๋ฐฉ์๋ง ์ฒ๋ฆฌํ๋ฉด ๋๋ฏ๋ก ์ดํดํ๊ธฐ ์ข๋ค.
-
๋ค์์ Reactjs์ ํจ๊ป Typescript๋ฅผ ์ฌ์ฉํ๋ฉด ์ป์ ์ ์๋ ์ฅ์ ์ ์ผ๋ถ์ด๋ค.
- ์ต์ Javascript ๊ธฐ๋ฅ์ ์ฌ์ฉํ ์ ์๋ค.
- ๋ณต์กํ ํ์ ์ ์๋ฅผ ์ํ ์ธํฐํ์ด์ค๋ฅผ ์ฌ์ฉํ ์ ์๋ค.
- VS Code์ ๊ฐ์ IDE๋ Typescript๋ฅผ ์ํด ๋ง๋ค์ด์ก๋ค.
- ๊ฐ๋ ์ฑ ๋ฐ ๊ฒ์ฆ์ด ์ฉ์ดํ์ฌ ๋ฒ๊ทธ ๋ฐฉ์ง๊ฐ ๊ฐ๋ฅํ๋ค.
์ฌ์ฉ์๊ฐ ๋ก๊ทธ์ธ๋ ์ํ๋ก ์๋ก ๊ณ ์นจ ํ ๋, ์ผ๋ฐ์ ์ผ๋ก ์ํ๋ฅผ ์ ์งํ๊ธฐ ์ํด main App.js์ useEffect hooks์์ load user action์ ์ถ๊ฐํ๋ค. Redux๋ฅผ ์ฌ์ฉํ๋ ๋์, loadUser action์ ์ฝ๊ฒ ์ ๊ทผํ ์ ์๋ค.
App.js
import { loadUser } from '../actions/auth';
store.dispatch(loadUser());
- ๊ทธ๋ฌ๋ Context API๋ฅผ ์ฌ์ฉํ์ฌ App.js์ context์ ์ก์ธ์คํ๋ ๋์, App.js๊ฐ auth context์ ์ก์ธ์คํ ์ ์๋๋ก index.js์์ AuthState๋ก ๊ฐ์ธ์ผ ํ๋ค. ์ด์ ํ์ด์ง๊ฐ ๋ค์ ๋ก๋๋ ๋๋ง๋ค ์ด๋ค ๊ฒฝ๋ก์ ์๋ , ์ฌ์ฉ์๋ loadUser action์ด ํธ๋ฆฌ๊ฑฐ ๋ ๊ฒฝ์ฐ ๊ฐ๊ฐ์ด re-render๋จ์ผ๋ก์ ์ธ์ฆ๋๋ค.
index.js
import React from 'react';
import ReactDOM from 'react-dom';
import App from './App';
import AuthState from './context/auth/AuthState'
ReactDOM.render(
<React.StrictMode>
<AuthState>
<App />
</AuthState>
</React.StrictMode>,
document.getElementById('root')
);
App.js
const authContext = useContext(AuthContext);
const { loadUser } = authContext;
useEffect(() => {
loadUser();
},[])
loadUser
const loadUser = async () => {
const token = sessionStorage.getItem('token');
if(!token){
dispatch({
type: ERROR
})
}
setAuthToken(token);
try {
const res = await axios('/api/auth');
dispatch({
type: USER_LOADED,
payload: res.data.data
})
} catch (err) {
console.error(err);
}
}
3๊ฐ์ง์ ์๋ก์ด JSX transform ์ฃผ์ ์ฅ์ ์ด ์๋ค.
- React packages๋ฅผ ๊ฐ์ ธ์ค์ง ์๊ณ JSX๋ฅผ ์ฌ์ฉํ ์ ์๋ค.
- ๋ ์์ ์ฌ์ด์ฆ๋ก ์ปดํ์ผ ๊ฒฐ๊ณผ๋ฌผ์ด ๋ง๋ค์ด์ง๋๋ก ๊ฐ์ ๋์๋ค.
- ์ถํ React๋ฅผ ๋ฐฐ์ฐ๋๋ฐ ํ์ํ ๊ฐ๋ ์ ์ค์ผ ์ ์๋๋ก ์ ์ฐํจ์ ์ ๊ณตํ๋ค.
์๋ก์ด JSX transform์ scope ๋ด React๊ฐ ํ์ ์๋ค. ์ฆ, ๊ฐ๋จํ ๊ฒฝ์ฐ์๋ React ํจํค์ง๋ฅผ ๊ฐ์ ธ์ฌ ํ์ ์๋ค.
์์ transform๊ณผ ์๋ก์ด transform์ ์ฃผ์ ์ฐจ์ด์ ์ ์ดํด๋ณด์.
Old Transform:
import React from 'react';
function App() {
return <h1>Good morning!!</h1>;
}
์ด์ JSX transform์ ์๋์ ๊ฐ์ด ์ผ๋ฐ์ ์ธ JavaScript๋ก ๋ณํํ๋ค.
import React from 'react';
function App() {
return React.createElement('h1', null, 'Good morning!!');
}
New Transform:
์๋ก์ด JSX transform์๋ React๊ฐ ํ์ํ์ง ์๋ค.
function App() {
return <h1>Good morning!!</h1>;
}
์์ ์ฝ๋๋ JSX transform์ ์๋์ ์ฝ๋๋ก ์ปดํ์ผ๋๋ค.
import {jsx as _jsx} from 'react/jsx-runtime';
function App() {
return _jsx('h1', { children: 'Good morning!!' });
}
Note: Hook์ ์ฌ์ฉํ๋ ค๋ฉด ์ฌ์ ํ React๋ฅผ ๊ฐ์ ธ์์ผ ํ๋ค.
Redux ํ์ create-react-app ํ๋ก์ ํธ๋ฅผ ์ํด ๊ณต์์ผ๋ก redux+js, redux+typescript ํฌํ๋ฆฟ์ ์ ๊ณตํ๋ค. ์์ฑ๋ ํ๋ก์ ํธ์๋ ์๋์ ํญ๋ชฉ์ด ํฌํจ๋๋ค.
- Redux Toolkit๊ณผ React-Redux ์์กด์ฑ.
- Redux store ์์ฑ ๋ฐ ์ค์ .
- React-Redux
<Provider>
๊ฐ store๋ฅผ React components์ ์ ๋ฌ. - redux logic ๋ฐ React-Redux hooks API๋ฅผ ์ถ๊ฐํ์ฌ components์์ store์ ์ํธ ์์ฉํ๋ ๋ฐฉ๋ฒ์ ๋ณด์ฌ์ฃผ๋ ์์ "counter" ์์ .
์๋ ๋ช ๋ น์ด๋ ๋ค์๊ณผ ๊ฐ์ด ํ ํ๋ฆฟ ์ต์ ๊ณผ ํจ๊ป ์คํ๋์ด์ผ ํ๋ค.
- Javascript template:
npx create-react-app my-app --template redux
- Typescript template:
npx create-react-app my-app --template redux-typescript
React Server Component๋ React app ์ฑ๋ฅ์ ํฅ์ ์ํฌ ๋ชฉ์ ์ผ๋ก server-side์์ ๋ ๋๋ง ๋ React ์ปดํฌ๋ํธ๋ฅผ ์์ฑํ๋ ๋ฐฉ๋ฒ์ด๋ค. ์ด๋ฌํ ์ปดํฌ๋ํธ๋ ๋ฐฑ์๋์์ ์ปดํฌ๋ํธ๋ฅผ ๋ก๋ํ ์ ์๋ค.
Note: React Server Components๋ ์์ง ๊ฐ๋ฐ ์ค์ด๋ฉฐ ์์ง ํ๋ก๋์ ์ ๊ถ์ฅ๋์ง ์๋๋ค.
Prop Drilling์ data๊ฐ ํ์ํ์ง ์์ง๋ง, ์ค์ง ์ ๋ฌํ๋ ๋ฐ ๋์์ด ๋๋ ์ปดํฌ๋ํธ๋ฅผ ํตํด React Component tree์ component์์ ๋ค๋ฅธ component๋ก data๋ฅผ ์ ๋ฌํ๋ ํ๋ก์ธ์ค์ ๋๋ค.