/React

๐Ÿ“Œ React.js study repository

Primary LanguageJavaScript

๐Ÿ“Œ React

React.js study repository ๐Ÿš€


  • react library
  1. react-filepond : file upload library

usage

import { FilePond, registerPlugin } from "react-filepond"
import FilePondPluginImageExifOrientation from "filepond-plugin-image-exif-orientation"
import FilePondPluginImagePreview from "filepond-plugin-image-preview"
import 'filepond/dist/filepond.min.css'
import 'filepond-plugin-image-preview/dist/filepond-plugin-image-preview.min.css'
:
render() {
    return (
      <div className="App">
        {/* Pass FilePond properties as attributes */}
        <FilePond
          ref={ref => (this.pond = ref)}
          files={this.state.files}
          allowMultiple={true}
          maxFiles={3}
          server="/api"
          oninit={() => this.handleInit()}
          onupdatefiles={fileItems => {
            // Set currently active file objects to this.state
            this.setState({
              files: fileItems.map(fileItem => fileItem.file)
            });
          }}
        />
      </div>
    );
  }
  1. react-draft-wysiwyg

usage

import React, { Component } from 'react';
import { Editor } from 'react-draft-wysiwyg';

<Editor
  wrapperClassName="wrapper-class"
  editorClassName="editor-class"
  toolbarClassName="toolbar-class"
  wrapperStyle={<wrapperStyleObject>}
  editorStyle={<editorStyleObject>}
  toolbarStyle={<toolbarStyleObject>}
/>
  1. react-text-mask

usage

<MaskedInput
  mask={['(', /[1-9]/, /\d/, /\d/, ')', ' ', /\d/, /\d/, /\d/, '-', /\d/, /\d/, /\d/, /\d/]}
  className="form-control"
  placeholder="Enter a phone number"
  //  you can input value
  value={this.state.number}
  guide={false}
  id="my-input-id"
  onBlur={() => {}}
  onChange={() => {}}
/>
  1. classNames

like Vue.js class style binding

classNames('foo', 'bar'); // => 'foo bar'
classNames('foo', { bar: true }); // => 'foo bar'
classNames({ 'foo-bar': true }); // => 'foo-bar'
classNames({ 'foo-bar': false }); // => ''
classNames({ foo: true }, { bar: true }); // => 'foo bar'
classNames({ foo: true, bar: true }); // => 'foo bar'

// lots of arguments of various types
classNames('foo', { bar: true, duck: false }, 'baz', { quux: true }); // => 'foo bar baz quux'

// other falsy values are just ignored
classNames(null, false, 'bar', undefined, 0, 1, { baz: null }, ''); // => 'bar 1'

1) shouldComponentUpdate ()

Life Cycle

  • render() ํ•จ์ˆ˜ ์‹คํ–‰ ์—ฌ๋ถ€๋ฅผ ๊ฐœ๋ฐœ์ž๊ฐ€ ๊ฒฐ์ •ํ•  ์ˆ˜ ์žˆ์Œ. ๋ถˆํ•„์š”ํ•œ ๋ Œ๋”๋ง์„ ๋ง‰์„ ์ˆ˜ ์žˆ์Œ.
import React, { Component } from 'react';
class TOC extends Component {
  shouldComponentUpdate (newProps, newState) {
    //  ๋ฐ”๋€ state์™€ state์˜ ๊ฐ’์„ ๋ฐ›์„ ์ˆ˜ ์žˆ์Œ
    if (this.props.data === newProps.data) {
      return false;
    }
    return true;
  }
}

2) setState()

props๋กœ ๋ฐ›์€ ๋‚ด์šฉ์„ ์ˆ˜์ •ํ•  ๋•Œ๋Š”, setState()๋กœ ์ˆ˜์ •ํ•ด์•ผํ•œ๋‹ค.

<header>
  <h1><a href="/" onClick={function(e) {
        this.setState({ mode: 'welcome' });
       }.bind(this)}>{ this.state.subject.title }</a>
  </h1>
</header>

shouldComponentUpdate()ํ•จ์ˆ˜๋ฅผ ์‚ฌ์šฉํ•œ๋‹ค๋ฉด concat, Array.from()๊ณผ ๊ฐ™์€ ํ•จ์ˆ˜๋ฅผ ์‚ฌ์šฉํ•ด์„œ ์›๋ณธ์„ ๋ฐ”๊พธ์ง€ ์•Š๋Š”๋‹ค.

  • arr copy : Array.from()
  • object copy : Object.assign()
var a = {name: 'tonz'};
var b = Object.assign({}, a); //  {name: 'tonz'}
var c = Object.assign({left: 1, right: 2}, a);  //  {left: 1, right: 2, name: 'tonz'}

3) Component

render ํ•จ์ˆ˜์˜ ๋ฆฌํ„ด ๊ฐ’์€ ํ•ญ์ƒ ์ตœ์ƒ์œ„ ํƒœ๊ทธ๋ถ€ํ„ฐ ์‹œ์ž‘ํ•ด์•ผํ•œ๋‹ค.

import React, { Component } extends from 'react';

class Subject extends Component {
  render () {
    return (
      <header>
        <h1>WEB</h1>
        world wide web!
      </header>
    );
  }
}

class App extends Component {
  render () {
    return (
      <div classNAme="App">
        <Subject></Subject>
      </div>
    );
  }
}

export default App;

3) props

  • Subject.js

๋‚ด๋ถ€์—์„œ this.props๋กœ ์ ‘๊ทผํ•œ๋‹ค.

class Subject extends Component {
  render () {
    return (
      <header>
        <h1>{ this.props.title }</h1>
        { this.props.sub }
      </header>
    );
  }
}
  • App.js
class App extends Component {
  render () {
    return (
      <div classNAme="App">
        <Subject title="WEB" sub="world wide web!"></Subject>
      </div>
    );
  }
}

4) directory structure

src
  -components
    -Subject.js
    :
  -App.js
  • Subject.js

impornt & export

import React, { Component } extends from 'react';

class Subject extends Component {
  render () {
    return (
      <header>
        <h1>{ this.props.title }</h1>
        { this.props.sub }
      </header>
    );
  }
}

export default Subject;
  • App.js
import Subject from './Components/Subject'

class App extends Component {
  render () {
    return (
      <div classNAme="App">
        <Subject title="WEB" sub="world wide web!"></Subject>
      </div>
    );
  }
}

5) state

  • constructure()

์ปดํฌ๋„ŒํŠธ๊ฐ€ ์‹คํ–‰๋  ๋•Œ, ๊ฐ€์žฅ ๋จผ์ € ์‹คํ–‰๋˜๋ฉฐ ์ดˆ๊ธฐํ™”๋ฅผ ๋‹ด๋‹นํ•œ๋‹ค. ์ƒ์œ„ ์ปดํฌ๋„ŒํŠธ ๊ฐ’์„ ํ•˜์œ„ ์ปดํฌ๋„ŒํŠธ๋กœ ์ „๋‹ฌํ•˜๊ณ  ์‹ถ์„ ๋•Œ, state๋ฅผ ์ด์šฉ.

import Subject from './Components/Subject'

class App extends Component {
  constructure (props) {
    super(props);
    this.state = {
      subject: {title: 'WEB', sub: 'world wide web!'}
    }
  }
  render () {
    return (
      <div classNAme="App">
        <Subject
          title={this.state.subject.title}
          sub={this.state.subject.sub}>
        </Subject>
      </div>
    );
  }
}

state ๊ฐ’์ด ๊ฐฑ์‹ ๋˜๋ฉด render()๊ฐ€ ์‹คํ–‰๋˜๋ฉฐ, ๋ณ€๊ฒฝ๋œ ๋ถ€๋ถ„๋งŒ update๋œ๋‹ค.

class Clock extends Component {
  constructure (props) {
    super(props);
    this.launchClock
    this.state = {
      //  ์ดˆ๊ธฐ ์ƒํƒœ์— ํ˜„์žฌ ์‹œ๊ฐ ์ถ”๊ฐ€
      currentTime: (newDate()).toLocaleString('en')
    }
  }
  launchClock () {
    setInterval(() => {
      this.setState({
        //  ๋งค ์ดˆ๋งˆ๋‹ค ํ˜„์žฌ์‹œ๊ฐ์œผ๋กœ ์ƒํƒœ ๊ฐฑ์‹ 
        currentTime: (newDate()).toLocaleString('en')
      });
    }, 1000);
  }
  render () {
    return (
      <div>
      { this.state.currentTime }
      </div>
    );
  }
}
  • key

TOC ์ปดํฌ๋„ŒํŠธ์—์„œ data[i] ๋“ฑ์˜ ๋ฐฉ์‹์œผ๋กœ ์ ‘๊ทผ ๊ฐ€๋Šฅํ•จ. ๊ฐ ๋ฐฐ์—ด์˜ ์š”์†Œ๋Š” key๋กœ ๊ตฌ๋ถ„ํ•˜๋ฉด๋จ. vue์˜ v-for์˜ key๊ฐ’๊ณผ ๋™์ผ.

class App extends Component {
  constructure (props) {
    super(props);
    this.state = {
      subject: {title: 'WEB', sub: 'world wide web!'},
      contents: [
        {id: 1, title: 'HTML', desc: 'HTML is HyperText...'}
        {id: 2, title: 'CSS', desc: 'CSS is for design'}
        {id: 3, title: 'JS', desc: 'JS JS JS'}
      ]
    }
  }
  render () {
    return (
      <div classNAme="App">
        <Subject
          title={this.state.subject.title}
          sub={this.state.subject.sub}>
        </Subject>
        <TOC data={this.state.contents}></TOC>
      </div>
    );
  }
}

6) JSX

  • if/else statement in JSX
render () {
  let sessionFlag = this.props.user.session //  bull
  return (
    <div>
      <a href={(sessionFlag) ? '/logout' : '/login'}>
        {(sessionFlag) ? 'Logout' : 'Login'}
      </a>
    </div>
  )
}

๋‹ค์Œ์€ ํ‹€๋ฆฐ ๊ตฌ๋ฌธ์ด๋‹ค. ์ค‘๊ด„ํ˜ธ ๋‚ด์—์„œ return๋ฌธ์„ ์‚ฌ์šฉํ•˜์ง€ ์•Š๋Š”๋‹ค. ์‚ผํ•ญ ์—ฐ์‚ฐ์ž๋ฅผ ์‚ฌ์šฉํ•œ๋‹ค.

class={if(!this.props.admin) return 'hide'}
  • JSX is converted to javascipt

  • data-์†์„ฑ

์‚ฌ์šฉ์ž ์ •์˜ ์†์„ฑ์„ ์ด์šฉํ•  ๋•Œ, data- prefix๋ฅผ ๋ถ™ํžˆ์ง€ ์•Š์œผ๋ฉด React์—์„œ๋Š” ๋ Œ๋”๋ง๋˜์ง€ ์•Š๋Š”๋‹ค. HTML ํ‘œ์ค€ ์†์„ฑ์ด ์•„๋‹ˆ๊ธฐ ๋•Œ๋ฎจ.

//  rendering OK in React
<li data-object-id="321"></li>

//  not rendering in React
<li object-id="321"></li>
  • style

๋ฌธ์ž์—ด ๋Œ€์‹  ์ž๋ฐ”์Šคํฌ๋ฆฝํŠธ ๊ฐ์ฒด๋ฅผ ์ „๋‹ฌํ•œ๋‹ค. css๋Š” ์นด๋ฉœ์ผ€์ด์Šค ํ‘œ๊ธฐ

let smallFontSize = {fontSize: '10px'}
<input style={smallFontSize}>
  • class & for

React์™€ JSX๋Š” class, for๋ฅผ ์ œ์™ธํ•˜๋ฉด ํ‘œ์ค€ HTML ์†์„ฑ์„ ๋ชจ๋‘ ์‚ฌ์šฉํ•  ์ˆ˜ ์žˆ๋‹ค.

<div className="hidden">...</div>
:
<label htmlFor={this.props.name}>
  {this.props.label}
</label>
:
  • Boolean ๊ฐ’์„ ์†์„ฑ ๊ฐ’์œผ๋กœ ์‚ฌ์šฉํ•˜๋Š” ๊ฒฝ์šฐ

disabled, required, checked, autofocus, readOnly์™€ ๊ฐ™์€ ์ผ๋ถ€ ์†์„ฑ์€ ํผ ์š”์†Œ์—๋งŒ ์‚ฌ์šฉํ•œ๋‹ค. ์†์„ฑ ๊ฐ’์€ ๋ฐ˜๋“œ์‹œ {} ์•ˆ์— ์ž๋ฐ”์Šคํฌ๋ฆฝํŠธ ์‹์œผ๋กœ ์ž‘์„ฑํ•ด์•ผ ํ•œ๋‹ค.

//  GOOD
<input disabled={false} />
//  XXX !!!!
<input disabled="false" />

ex) React ์ ์šฉ๊ฐ€์ด๋“œ

1) React ์ปดํฌ๋„ŒํŠธ ๋งŒ๋“ค๊ธฐ: ํ•˜์œ„ React ์ปดํฌ๋„ŒํŠธ๋กœ prop(or state)์™€ dispatch() ๋ฉ”์„œ๋“œ๋ฅผ ์ „๋‹ฌํ•œ๋‹ค.

const todolistStateToProps = (state) => {
  return {
    todos: state.todos
  }
}

const todolistDispatchToProps = (dispatch) => {
    return {
        onClick(data){
          dispatch(complete(data))
        }
    }
}
//  connect์˜ ์ฒซ๋ฒˆ์งธ parameter๋Š” state, ๋‘๋ฒˆ์งธ parameter๋Š” dispatch ํ•จ์ˆ˜
export default connect(todolistStateToProps,todolistDispatchToProps)(TODOList);

2) ์•ก์…˜ ๋ช…๋ น์–ด์™€ ์•ก์…˜ ๋ฉ”์„œ๋“œ ๋งŒ๋“ค๊ธฐ: state ๋ณ€๊ฒฝ๊ณผ ๋น„๋™๊ธฐ ์ฒ˜๋ฆฌ๋ฅผ ๊ตฌํ˜„ํ•œ๋‹ค.

function complete({complete, id}) {
	return { type: COMPLETE_TODO,  complete, id};
}

function complete2(data2) {
  return (dispatch) => {
    return fetch("api/add.json").then(
      res => res.json().then(data => dispatch(complete(data2)))
    );
  };
}

3) ๋ฆฌ๋“€์„œ ์ƒ์„ฑ: ์Šคํ† ์–ด์˜ ๊ตฌ์กฐ๋ฅผ ์ •ํ•œ๋‹ค.

const todos = (state = [], action) => {
  switch (action.type) {
    case ADD_TODO:
      return [
        ...state, todo(undefined, action)
      ];
    case COMPLETE_TODO:
      return state.map(t => todo(t, action));
    default:
      return state;
  }
}

4) dispatch() ๋ฉ”์„œ๋“œ์— ์•ก์…˜ ๊ฒฐ๊ณผ ์ „๋‹ฌ: ์•ก์…˜์˜ ๊ฒฐ๊ณผ๋ฅผ ์ „๋‹ฌํ•œ๋‹ค.


ex) Redux์˜ ์ดํ•ด

  • store

ํ•œ ๊ฐœ์˜ ๊ณ„์ธตํ˜• ๊ฐ์ฒด. ์• ํ”Œ๋ฆฌ์ผ€์ด์…˜์˜ state๋ฅผ ๊ฐ€์ง€๊ณ  ์žˆ๋‹ค.

  • React Component

stae์— ๋งž๊ฒŒ ํ™”๋ฉด์„ ๊ทธ๋ฆฐ๋‹ค.

  • action creator

์‚ฌ์šฉ์ž๊ฐ€ ๋ฐœ์ƒ์‹œํ‚ค๋Š” ์ด๋ฒคํŠธ์— ๋งž์ถฐ ์Šคํ† ์–ด์— ์ „๋‹ฌํ•  ์•ก์…˜์„ ๋งŒ๋“ ๋‹ค. ์•ก์…˜์ƒ์„ฑ์ž๊ฐ€ ๋งŒ๋“œ๋Š” ์•ก์…˜์€ [์ˆœ์ˆ˜๊ฐ์ฒด]๋‹ค.

  • reducer

ํ˜„์žฌ state์™€ ์•ก์…˜์„ ์ „๋‹ฌ๋ฐ›์•„ ์ƒˆ๋กœ์šด state๋ฅผ ๋ฐ˜ํ™˜ํ•œ๋‹ค. ๋ฆฌ๋“€์„œ๋Š” [์ˆœ์ˆ˜ ํ•จ์ˆ˜]๋‹ค.

class DatePicker extends Component {
 :
}
:
DatePicker.propTypes {
 currentDate: PropTypes.string.isRequired,
 rows: PropTypes.number,
 // ์—ด๊ฑฐํ˜•(enum)์œผ๋กœ ์ฒ˜๋ฆฌํ•˜์—ฌ prop๊ฐ€ ํŠน์ • ๊ฐ’๋“ค๋กœ ์ œํ•œ๋˜๋„๋ก ํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.
 locale: PropTypes.oneOf(['US', 'KO']),
}

9) HOC

์ฝ”๋“œ ์žฌ์‚ฌ์šฉ์„ ์œ„ํ•œ React ๊ณ ์ฐจ ์ปดํฌ๋„ŒํŠธ

const LoadWebSite = (Component) => {
 :
 class _LoadWebSite extends Component {
  :
  _LoadWebSite.displayName = 'Enhanced Component'
  return _LoadWebSite
 } 
}

10) ์ปจํ…Œ์ด๋„ˆ ์ปดํฌ๋„ŒํŠธ & ํ”„๋ ˆ์  ํ…Œ์ด์…˜ ์ปดํฌ๋„ŒํŠธ

  • ํ”„๋ ˆ์  ํ…Œ์ด์…˜ ์ปดํฌ๋„ŒํŠธ

DOM๊ณผ ์Šคํƒ€์ผ์— ๊ตฌ์กฐ๋งŒ ์ถ”๊ฐ€ํ•œ๋‹ค. ์†์„ฑ(props)๋Š” ์‚ฌ์šฉํ•˜์ง€๋งŒ ์ƒํƒœ(state)๋ฅผ ๊ฐ–๋Š” ๊ฒฝ์šฐ๋Š” ๊ฑฐ์˜ ์—†๋‹ค.

11) FORM

1) input type checkbox

  • defaultChecked๋Š” initail render์—๋งŒ call๋˜๋ฏ€๋กœ update๊ฐ€ ํ•„์š”ํ•˜๋ฉด checked๋ฅผ ์‚ฌ์šฉํ•ด์•ผํ•œ๋‹ค.
<input type="checkbox"
       name="tomato"
       checked={ this.state.input === 1 }
       // value๊ฐ’์€ state๊ฐ’์ด ์•„๋‹Œ ์ง€์ •๊ฐ’์œผ๋กœ setting๊ฐ€๋Šฅ
       value={ 1 }
       innerRef={ (input) => this.tomatoInput = input }
       onChange={ this.selectInput }
       disabled={ isInputDisabled } /> TOMATO
       :
       
selectInput (e) {
 let { value } = e.target
 var inputValue = Number(value)
 this.setState({ input: inputValue })
}