본문 바로가기
IT/React

React Components

by DOSGamer 2022. 7. 27.
반응형

React 에서 <h1>Hello World!</h1> 을 injecting 하려면

import React from 'react'
import ReactDOM from 'react-dom'

ReactDOM.render(<h1>Hello World!</h1>, document.getElementById('app'))

ReactDOM.render(
  React.DOM.h1(null, 'Hello World!'),
  document.getElementById('app')
)

 

커스텀 컴포넌트

// hooks 방식
const BlogPostExcerpt = () => {
  return (
    <div>
      <h1>Title</h1>
      <p>Description</p>
    </div>
  )
}

// class 방식
import React, { Component } from 'react'

class BlogPostExcerpt extends Component {
  render() {
    return (
      <div>
        <h1>Title</h1>
        <p>Description</p>
      </div>
    )
  }
}

 

State

constructor 에서 기본값을 설정합니다

class BlogPostExcerpt extends Component {
  constructor(props) {
    super(props)
    this.state = { clicked: false }
  }
  
  render() {
    return (
      <div>
        <h1>Title</h1>
        <p>Description</p>
        <p>Clicked: {this.state.clicked}</p>
      </div>
    )
  }
}

상태 변경

// 이렇게 사용하면 안됩니다
this.state.clicked = true

이렇게 사용하면 안됩니다.

반드시 setState 를 사용해야 합니다

this.setState({ clicked: true })

 

setState 를 사용해야 하는 이유

React 가 state 의 변경을 알고 있어야 하고 React 가 그런 변경을 모아서 실제 DOM 을 업데이트 해야 합니다

두개의 컴포넌트가 state 를 공유하는 경우

Unidirectional Data Flow 원칙 때문에 컴포넌트 간에 데이터 공유시에 데이터가 변경되어야 하는 경우는

상위 컴포넌트가 state 를 가지고 있어야 하고 state 에 대한 변경 이벤트도 상위 컴포넌트에 있어야 함

// 금액단위를 Display , CurrencySwitcher 컴포넌트가 같이 사용하고 있다

class Converter extends React.Component {
  constructor(props) {
    super(props)
    this.state = { currency: '€' }
  }
  
  render() {
    return (
      <div>
        <Display currency={this.state.currency} />
        <CurrencySwitcher currency={this.state.currency} />
      </div>
    )
  }
}
// CurrencySwitcher 컴포넌트가 통화를 변경하기 위해서
// handleChangeCurrency 를 만들어서 자식 컴포넌트에게 전달해준다

class Converter extends React.Component {
  constructor(props) {
    super(props)
    this.state = { currency: '€' }
  }
  
  handleChangeCurrency = event => {
    this.setState({ currency: this.state.currency === '€' ? '$' : '€' })
  }
  
  render() {
    return (
      <div>
        <Display currency={this.state.currency} />
        <CurrencySwitcher
          currency={this.state.currency}
          handleChangeCurrency={this.handleChangeCurrency}
        />
      </div>
    )
  }
}

const CurrencySwitcher = props => {
  return (
    <button onClick={props.handleChangeCurrency}>
      Current currency is {props.currency}. Change it!
    </button>
  )
}

const Display = props => {
  return <p>Current currency is {props.currency}.</p>
}

 

Props

컴포넌트가 속성 데이터를 얻는 방법이다

class 컴포넌트에서는 this.props 로 접근할 수 있다

props 는 자식 컴포넌트로 데이터를 전달하는 좋은 방법이지만 아래같은 경우 복잡해진다

  • 여러 수준 아래에 있는 자식 컴포넌트에서 state 를 사용해야 할 때 (중간에 있는 컴포넌트들이 전부 props 를 bypass 해줘야 함)
  • 완전히 관련이 없는 컴포넌트가 다른 컴포넌트의 state 를 사용해야 할 때
// function components
const BlogPostExcerpt = props => {
  return (
    <div>
      <h1>{props.title}</h1>
      <p>{props.description}</p>
    </div>
  )
}

// class components
import React, { Component } from 'react'

class BlogPostExcerpt extends Component {
  render() {
    return (
      <div>
        <h1>{this.props.title}</h1>
        <p>{this.props.description}</p>
      </div>
    )
  }
}

 

Props 전달 방법

HTML 의 속성과 비슷한 방식으로 props 를 전달합니다

const desc = 'A description'
//...
<BlogPostExcerpt title="A blog post" description={desc} />

Children

<BlogPostExcerpt title="A blog post" description="{desc}">
  Something
</BlogPostExcerpt>

Something 을 접근하려면 this.props.children 으로 접근해야 한다

Presentational vs container components

  • Presentational Components : UI 중심
  • Container Components : 데이터 처리 중심
// presentational component
const Users = props => (
  <ul>
    {props.users.map(user => (
      <li>{user}</li>
    ))}
  </ul>
)


// container component
class UsersContainer extends React.Component {
  constructor() {
    this.state = {
      users: []
    }
  }
  
  componentDidMount() {
    axios.get('/users').then(users =>
      this.setState({ users: users }))
    )
  }
  
  render() {
    return <Users users={this.state.users} />
  }
}

 

State vs props

  • state : 컴포넌트가 직접 관리 하는 변수
  • props : 부모 컴포넌트로 부터 받은 변수

 

PropTypes

props 의 type 을 강제해서 잘 못 된 값들이 넘어오는 것을 사전에 방지 할 수 있다

import PropTypes from 'prop-types'
import React from 'react'

class BlogPostExcerpt extends Component {
  render() {
    return (
      <div>
        <h1>{this.props.title}</h1>
        <p>{this.props.description}</p>
      </div>
    )
  }
}

BlogPostExcerpt.propTypes = {
  title: PropTypes.string,
  description: PropTypes.string
}

export default BlogPostExcerpt
  • PropTypes.array
  • PropTypes.bool
  • PropTypes.func
  • PropTypes.number
  • PropTypes.object
  • PropTypes.string
  • PropTypes.symbol
// 둘 중 하나 허용
PropTypes.oneOfType([
  PropTypes.string,
  PropTypes.number
]),

// 여러 유형 허용
PropTypes.oneOf(['Test1', 'Test2']),

// 클래스의 인스턴스 허용
PropTypes.instanceOf(Something)

// React 의 node 허용
PropTypes.node

// 모든 타입 허용
PropTypes.any

// 배열도 적용가능
PropTypes.arrayOf(PropTypes.string)

// 오브젝트도 가능
PropTypes.shape({
  color: PropTypes.string,
  fontSize: PropTypes.number
})

// 속성 누락시에 오류를 내도록 필수로 설정 가능
PropTypes.arrayOf(PropTypes.string).isRequired,
PropTypes.string.isRequired,

 

React Fragment

컴포넌트는 1개의 element 를 return 해야 합니다. 

return 시에 최상위 태그를 React.Fragment  또는 <></>   를 사용합니다

import React, { Component } from 'react'

class BlogPostExcerpt extends Component {
  render() {
    return (
      <>
        <h1>{this.props.title}</h1>
        <p>{this.props.description}</p>
      </>
    )
  }
}

 

이벤트 처리

camelCase 를 사용하여 이벤트를 처리 합니다

const CurrencySwitcher = props => {
  return (
    <button onClick={props.handleChangeCurrency}>
      Current currency is {props.currency}. Change it!
    </button>
  )
}

이벤트 핸들러를 컴포넌트 함수로 정의하는 것이 일반적입니다

class Converter extends React.Component {
  handleChangeCurrency = event => {
    this.setState({ currency: this.state.currency === '€' ? '$' : '€' })
  }
}

클래스 컴포넌트를 사용시에는 this 를 바인딩 하세요

class Converter extends React.Component {
  handleClick = e => {
    /* ... */
  }
  //...
}


// 아니면 constructor 에서 this 를 바인딩 해줘야 함
class Converter extends React.Component {
  constructor(props) {
    super(props)
    this.handleClick = this.handleClick.bind(this)
  }
  handleClick(e) {}
}

 

사용가능한 이벤트 리스트

Clipboard

  • onCopy
  • onCut
  • onPaste

Composition

  • onCompositionEnd
  • onCompositionStart
  • onCompositionUpdate

Keyboard

  • onKeyDown
  • onKeyPress
  • onKeyUp

Focus

  • onFocus
  • onBlur

Form

  • onChange
  • onInput
  • onSubmit

Mouse

  • onClick
  • onContextMenu
  • onDoubleClick
  • onDrag
  • onDragEnd
  • onDragEnter
  • onDragExit
  • onDragLeave
  • onDragOver
  • onDragStart
  • onDrop
  • onMouseDown
  • onMouseEnter
  • onMouseLeave
  • onMouseMove
  • onMouseOut
  • onMouseOver
  • onMouseUp

Selection

  • onSelect

Touch

  • onTouchCancel
  • onTouchEnd
  • onTouchMove
  • onTouchStart

UI

  • onScroll

Mouse Wheel

  • onWheel

Media

  • onAbort
  • onCanPlay
  • onCanPlayThrough
  • onDurationChange
  • onEmptied
  • onEncrypted
  • onEnded
  • onError
  • onLoadedData
  • onLoadedMetadata
  • onLoadStart
  • onPause
  • onPlay
  • onPlaying
  • onProgress
  • onRateChange
  • onSeeked
  • onSeeking
  • onStalled
  • onSuspend
  • onTimeUpdate
  • onVolumeChange
  • onWaiting

Image

  • onLoad
  • onError

Animation

  • onAnimationStart
  • onAnimationEnd
  • onAnimationIteration

Transition

  • onTransitionEnd

 

이벤트의 생성주기

  • Mounting
    • Constructor : 컴포넌트를 마운팅 할 때 첫번째로 호출
    • getDerivedStateFromProps() : 
    • render()
    • componentDidMount() : API 호출을 수행하거나 DOM 에서 작업을 처리할 때 사용합니다
  • Updating
    • getDerivedStateFromProps()
    • shouldComponentUpdate()
    • render()
    • getSnapshotBeforeUpdate()
    • componentDidUpdate() : 컴포넌트가 DOM 에서 업데이트 되었을 때 호출 합니다
  • Unmounting
    • componentWillUnmount() : 컴포넌트가 DOM 에서 제거될 때 호출

 

 

React 에서 Forms 사용방법

React 에서 Form 을 이용하여 데이터를 처리 하는 두가지 방법( DOM 이 data 를 핸들링 하는 경우와 컴포넌트가 data 를 핸들링 하는 경우 ) 이 있습니다

uncontrolled components vs controlled components

  • controlled components : data 를 컴포넌트가 핸들링
  • uncontrolled components : data 를 DOM 이 핸들링 (input elements 에서 states 를 사용하지 않는다)
    • input type : file, date

 

controlled components 의 예

value 와 onChange 를 이용해서 state 정보를 관리 합니다

 handleChange 에서 이전값과 현재값을 확인할 수 있기에 여기서 validation 체크를 해줍니다

class Form extends React.Component {
  constructor(props) {
    super(props)
    this.state = { username: '' }
    this.handleChange = this.handleChange.bind(this)
    this.handleSubmit = this.handleSubmit.bind(this)
  }
  
  handleChange(event) {
    this.setState({ value: event.target.value })
  }
  
  handleSubmit(event) {
    alert(this.state.username)
    event.preventDefault()
  }
  
  render() {
    return (
      <form onSubmit={this.handleSubmit}>
        <input
          type="text"
          value={this.state.username}
          onChange={this.handleChange}
        />
        <input type="submit" value="Submit" />
      </form>
    )
  }
}

textarea 의 예제

<textarea value={this.state.address} onChange={this.handleChange} />

select 의 예제

<select value="{this.state.age}" onChange="{this.handleChange}">
  <option value="teen">Less than 18</option>
  <option value="adult">18+</option>
</select>

 

uncontrolled components 의 예

file, date 같은 경우는 React.createRef() 를 이용해서 필드에 대한 참조를 생성해서 사용해야 합니다

constructor 에서 참조를 생성하고 binding 하고
input file 에 생성한 참조를 연결하고
onSubmit 에서 binding Submit 을 연결하고
handleSubmit 에서 참조를 이용하여 file 을 확인해서 처리해준다

class FileInput extends React.Component {
  constructor(props) {
    super(props)
    this.curriculum = React.createRef()
    this.handleSubmit = this.handleSubmit.bind(this)
  }
  
  handleSubmit(event) {
    alert(this.curriculum.current.files[0].name)
    event.preventDefault()
  }
  
  render() {
    return (
      <form onSubmit={this.handleSubmit}>
        <input type="file" ref={this.curriculum} />
        <input type="submit" value="Submit" />
      </form>
    )
  }
}

아래 date 의 경우도

constructor 에서 inputRef 참조를 생성하고 
input date 에 생성한 inputRef 를 연결
onSubmit 에 직접 함수를 생성하여 처리해줬다
(binding 함수를 만들어서 연결하지는 않고 직접 함수를 작성)

import React, { Component } from "react";

class UncontrolledComponent extends Component {
  constructor(props) {
    super(props);
    this.inputRef = React.createRef();
  }

  render() {
    return (
      <form
        onSubmit={(event) => {
          event.preventDefault();
          console.log("Input value - ", this.inputRef.current.value);
        }}
      >
        <div>
          <label>Uncontrolled input </label>
          <input type="date" name="data" id="date-input" ref={this.inputRef} />
        </div>

        <button type="submit">Submit</button>
      </form>
    );
  }
}
export default UncontrolledComponent;

state 를 DOM 에서 직접 저장되고 처리됩니다 (컴포넌트에서 처리 하지 않음)

 

이런 번거로운 Form 처리 방식을 개선하는 라이브러리로 formik 이 있습니다  https://formik.org/   

 

Formik

React hooks and components for hassle-free form validation. The world's leading companies use Formik to build forms and surveys in React and React Native.

formik.org

 

DOM element 의 참조 만드는 방법

DOM 을 직접 건드리는 것보다 참조를 만들어서 액세스 하는 것이 좋다고 합니다

참조를 만드는 방법

ref={el => this.someProperty = el}
class SomeComponent extends Component {
  render() {
    return <button ref={el => (this.button = el)} />
  }
}

 

반응형

'IT > React' 카테고리의 다른 글

React Native 벡터 아이콘 사용하기 vetor-icon  (0) 2022.08.05
React 18 에는 enzyme 사용하지 마세요  (0) 2022.08.01
REACT NATIVE UI COMPONENT  (0) 2022.08.01
Context API React  (0) 2022.08.01
React JSX  (0) 2022.07.27
React 기본개념  (0) 2022.07.27
React 를 위한 자바스크립트 컨셉 이해하기  (0) 2022.07.27
React 개발 환경 설정  (0) 2022.07.26