리덕스를 사용한 상태 관리


왜 리덕스를 사용하는가?


컴포넌트를 프리젠테이셔널 컴포넌트컨테이너 컴포넌트로 나눠서 사용하면 사용자가 이용할 유저 인터페이스와 상태를 다루는 데이터가 분리되어 프로젝트를 이해하기 쉽고 컴포넌트 재사용률도 높습니다.


1) 프리젠테이셔널 컴포넌트
오직 뷰만 담당하는 컴포넌트로 UI 관련된 state외에는 state는 존재하면 안되며, props로만 데이터를 처리합니다.

2) 컨테이너 컴포넌트
프리젠테이셔널 컴포넌트들과 컨테이너 컴포넌트들의 관리를 담당하는 컴포넌트로 프리젠테이셔널 컴포넌트와 반대로 스타일은 존재하면 안됩니다.

리덕스

리덕스의 개념적인 부분은 아래 포스팅을 참고해주세요


예제


0) react-redux 라이브러리 설치

yarn을 사용하시는 분들은 yarn add react-redux
npm을 사용하시는 분들은 npm install react-redux로 설치 가능합니다.

1) 작업 디렉토리 생성

/actions : 액션타입, 액션 생성자 파일 
/components : 프리젠테이셔널 컴포넌트
/containers : 컨테이너 컴포넌트
/reducers : 스토어의 기본 상태값, 리듀서 파일
/lib : 일부 컴포넌트에서 함꼐 사용되는 파일

2) 프리젠테이셔널 컴포넌트 생성

좌클릭 시 증가, 우클릭 시 감소, 더블클릭 시 색 변경함수를 props로 전달받습니다.

/components/Counter.js

import React from 'react';
import PropTypes from 'prop-types';
import './Counter.css';

const Counter=({number, color, onIncrement, onDecrement, onSetColor})=>{
return (
<div
className = "Counter"
onClick={onIncrement}
onContextMenu={(e)=>{
e.preventDefault();
onDecrement();
}}
onDoubleClick={onSetColor}
style={{
backgroundColor : color
}}
>
{number}
</div>
);
};

Counter.propTypes ={
number : PropTypes.number,
color : PropTypes.string,
onIncrement : PropTypes.func,
onDecrement : PropTypes.func,
onSetColor : PropTypes.func
}
Counter.defaultProps ={
number : 0,
color : 'black',
onIncrement : ()=>console.warn('onIncrement not defined'),
onDecrement : ()=>console.warn('onDecrement not defined'),
onSetColor : ()=>console.warn('onSetColor not defined')
}

export default Counter;

/components/Counter.css

.Counter{
width: 10rem;
height: 10rem;
display: flex;
align-items: center;
justify-content: center;
margin:1rem;
color:white;
font-size:3rem;
border-radius:100%;
cursor:pointer;
user-select: none;
transition: background-color 0.75s;
}

3) 액션 생성

증가, 감소, 색상 변경 액션을 정의합니다.

/actions/ActionTypes.js

export const INCREMENT = 'INCREMENT';
export const DECREMENT = 'DECREMENT';
export const SET_COLOR = 'SET_COLOR';

정의한 액션을 만들어주는 함수를 만들어 줍니다.

/actions/index.js

import * as types from './ActionTypes';

export const increment = () => ({
type : types.INCREMENT
});
export const decrement = () => ({
type : types.DECREMENT
});
export const setColor = (color) => ({
type : types.SET_COLOR,
color
});


4) 리듀서 생성

액션의 type에 따라 변화를 일으키는 함수입니다. 최초 변화를 일으키기전에 가지고 있어야 하는 초기 상태(initai를 정의 해야합니다.

/reducers/index.js

import * as types from '../actions/ActionTypes';

const initialState = {
color : 'black'
, number : 0
};

function counter (state=initialState, action){
switch(action.type){
case types.INCREMENT:
return{
...state,
number:state.number+1
};
case types.DECREMENT:
return{
...state,
number:state.number-1
};
case types.SET_COLOR:
return{
...state,
color : action.color
};
default:
return state;
}
}

export default counter;



5) 스토어 생성


리덕스에서 가장 핵심적인 인스턴스로 현재 상태가 내장되어 있고, 상태를 업데이트 할 떄마다 구독중인 함수를 호출합니다.


리덕스에서 createStore를 불러와서 리듀서를 파라미터로 넣어 스토어를 생성합니다.


/src/index.js


import React from 'react';
import ReactDOM from 'react-dom';
import App from './containers/App';
import './index.css';

import {createStore} from 'redux';
import reducers from './reducers';

const store = createStore(reducers);

ReactDOM.render(
<App />
,document.getElementById('root')
);




6) Provider 컴포넌트로 리액트 앱에 store연동


Provider는 react-redux라이브러리에 내장된 리액트 애플리케이션에 손쉽게 스토어를 연동할 수 있도록 도와주는 컴포넌트 입니다.


Provider를 불러와서 최상위 컴포넌트를 감싸줍니다.


import {Provider} from 'react-redux';

ReactDOM.render(
<Provider store={store}>
<App />
</Provider>
,document.getElementById('root')
);




7) 컨테이너 컴포넌트 생성


react-redux의 connect 함수를 사용하여 스토어에 연결합니다.


connect([mapStateToProps], [mapDispatchToProps], [mergeProps])


mapStateToProps : store.getState() 결과 값인 state를 파라미터로 받아서 props로 사용할 객체를 반환

mapDispatchToProps : dispatch를 파라미터로 받아 액션을 디스패치하는 함수들을 객체 안에 넣어서 반환

mergeProps : state와 dispatch가 동시에 필요한 함수를 props로 전달해야할 때 사용(일반적으로 잘 사용하지는 않음)


/containers/CounterContainer.js


import Counter from '../components/Counter';
import * as actions from '../actions';
import { connect } from 'react-redux';

// 13가지 색상 중 랜덤으로 선택하는 함수
export function getRandomColor() {
const colors = [
'#495057',
'#f03e3e',
'#d6336c',
'#ae3ec9',
'#7048e8',
'#4263eb',
'#1c7cd6',
'#1098ad',
'#0ca678',
'#37b24d',
'#74b816',
'#f59f00',
'#f76707'
];

// 0부터 12까지 랜덤 숫자
const random = Math.floor(Math.random() * 13);

// 랜덤 색상 반환
return colors[random];
}

// store 안의 state 값을 props로 연결
const mapStateToProps = (state) => ({
color: state.color,
number: state.number
});

/*
액션 생성 함수를 사용하여 액션을 생성하고,
해당 액션을 dispatch하는 함수를 만든 후, 이를 props로 연결
*/
const mapDispatchToProps = (dispatch) => ({
onIncrement: () => dispatch(actions.increment()),
onDecrement: () => dispatch(actions.decrement()),
onSetColor: () => {
const color = getRandomColor();
dispatch(actions.setColor(color));
}
});

// Counter 컴포넌트의 Container 컴포넌트
// Counter 컴포넌트를 애플리케이션의 데이터 레이어와 묶는 역할
const CounterContainer = connect(
mapStateToProps,
mapDispatchToProps
)(Counter);

export default CounterContainer;


실행 결과





참고 : 리액트를 다루는 기술(저:VELOPERT)



'언어 > ReactJS' 카테고리의 다른 글

[React] 리덕스 개념  (0) 2019.05.24
[React] 함수형 컴포넌트  (0) 2019.05.21
[React] 라이프 사이클 함수  (0) 2019.05.07
[React] 컴포넌트 반복  (0) 2019.05.05
[React] Ref  (0) 2019.05.02

리덕스 개념


리덕스?

리액트의 상태를 더 효율적으로 관리하는 데 사용하는 상태 관리 라이브러리로 상태 관리의 로직을 컴포넌트 밖에서 처리합니다.

리액트에서 사용하려고 만든 라이브러리지만, 리액트에 의존하지 않기때문에 리액트를 사용하지 않아도 리덕스 사용 가능 합니다.


리덕스 관련 용어
1) 스토어 : 애플리케이션의 상태 값들을 내장
2) 액션 : 상태 변화를 일으킬 때 참조하는 객체로 반드시 type을 가져야 함
3) 디스패치 : 액션을 스토어에 전달하는 것
4) 리듀서 : 상태를 변화시키는 로직이 있는 함수
5) 구독 : 스토어 값이 필요한 컴포넌트는 스토어를 구독

그림으로 표현하면 아래와 같습니다.

리덕스 규칙

리덕스 공식 메뉴얼에서도 안내하는 규칙 세가지가 있습니다.


1) 스토어는 단 한개만 존재

2) state는 읽기 전용

3) 변화는 순수 함수로 구성





리덕스 사용

리덕스 실습은 JSBin 사이트를 활용해서 리액트 환경이 아닌 환경에서 실습을 진행 했습니다.

1) HTML에 Redux 관련 <script>태그 추가

<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width">
<title>JS Bin</title>
</head>
<body>
<script src="https://cdnjs.cloudflare.com/ajax/libs/redux/3.6.0/redux.js"></script>
</body>
</html>


2) console.log로 Redux 확인


아래와 같은 명령을 실행하면 정상적으로 추가했는지 확인 가능합니다.

console.log(Redux);


콘솔창 결과



3) action 추가


실습에서는 증가와 감소 액션을 만들어서 사용 했습니다.


const INCREMENT = 'INCREMENT';
const DECREMENT = 'DECREMENT';

const increment = (diff) => ({
type : INCREMENT
,diff : diff
});
const decrement = (diff) => ({
type : DECREMENT
,diff : diff
});


4) 리듀서 추가


리듀서는 변화를 일으키는 함수로 본 실습에서는 실질적으로 증·감하는 역할을 합니다.


state값을 초기화하는 함수도 같이 추가 했습니다.


const initialState = {
number : 0
};

function counter(state = initialState, action){
switch(action.type){
case INCREMENT:
return {number:state.number + action.diff};
case DECREMENT:
return {number:state.number - action.diff};
default :
return state;
}
}


5) 스토어 생성


파라미터로 리듀서를 넘겨줍니다.


const {createStore} = Redux;

const store = createStore(counter);


6) 구독

리액트에서 구독하는 작업은 react-defux의 connect 함수가 대신 하지만, 본 실습에서는  subscribe함수를 직접 사용 했습니다.


스토어의 상태가 변화할 때마다 호출 되고, unsubscribe함수를 반환하기 때문에 unsubscribe 함수를 호출하면 구독 취소가 가능합니다.


const unsubscribe = store.subscribe(()=>{
console.log(store.getState());
});


7) 수행


dispatch함수를 통해 액션을 넘겨줍니다.


store.dispatch(increment(1));
store.dispatch(increment(10));
store.dispatch(decrement(5));


콘솔창 결과




참고 : 리액트를 다루는 기술(저:VELOPERT)


'언어 > ReactJS' 카테고리의 다른 글

[React ]리덕스를 사용한 상태 관리  (3) 2019.06.14
[React] 함수형 컴포넌트  (0) 2019.05.21
[React] 라이프 사이클 함수  (0) 2019.05.07
[React] 컴포넌트 반복  (0) 2019.05.05
[React] Ref  (0) 2019.05.02

함수형 컴포넌트


함수형 컴포넌트?

지금까지 리액트에서 컴포넌트를 작성할 때 class 문법을 이용해서 정의를 했습니다. 라이프 사이클 API와 State를 사용할 필요가 없을경우, props만 전달받아 뷰를 렌더링 해서 간단하게 정의하는 컴포넌트를 의미합니다.


특징

1) 라이프사이클, State등 불필요한 기능을 제거한 상태이기 때문에 일반 클래스형 컴포넌트보다 메모리 소모량이 적고 성능도 조금 더 빠릅니다.

2) ES6의 화살표, 비구조화 할당 문법도 사용 가능합니다.


비교

일반 컴포넌트


import React, { Component } from 'react';

class test extends Component {
render() {
return (
<div>
TEST {this.props.number}
</div>
);
}
}

export default test;


함수형 컴포넌트


import React from 'react';

function Test(props){
return (
<div>
TEST {props.number}
</div>
);
}

export default Test;


함수형 컴포넌트 - ES6


import React from 'react';

const Test = ({num}) => (
<div>Test {num}</div>
)

export default Test;




참고 : 리액트를 다루는 기술(저:VELOPERT)

'언어 > ReactJS' 카테고리의 다른 글

[React ]리덕스를 사용한 상태 관리  (3) 2019.06.14
[React] 리덕스 개념  (0) 2019.05.24
[React] 라이프 사이클 함수  (0) 2019.05.07
[React] 컴포넌트 반복  (0) 2019.05.05
[React] Ref  (0) 2019.05.02

라이프 사이클 함수


라이프 사이클?

모든 리액트 컴포넌트에는 라이프 사이클(수명 주기)이 존재합니다. 라이프 사이클은 컴포넌트가 생성되고 소멸될 때까지 일련의 과정을 의미 합니다.


라이프 사이클 함수

총 10가지의 함수가 존재하며, will이 붙은 함수는 어떤 작업을 하기 전에 실행되고, did가 붙은 함수는 작업을 한 후에 실행됩니다.


라이프 사이클 함수를 사용 하는 경우

어떤 작업을 수행할 때 렌더링 할 때 처리해야 하는 경우가 있고 업데이트 전후로 처리해야 하는 경우가 있는데 이럴 경우 라이프 사이클 함수를 이용해서 처리합니다.


컴포넌트의 라이프 사이클

마운트(페이지에 컴포넌트가 나타남) -> 업데이트(리렌더링) -> 언마운트(페이지에서 컴포넌트가 사라짐)


마운트?

DOM이 생성되고 웹 브라우저상에 나타는 과정


업데이트? 

컴포넌트를 업데이트 하는 경우로 아래와 같은 경우에 동작합니다.

1) Props가 바뀔 경우

2) state가 바뀔 경우

3) 부모 컴포넌트가 리렌더링 할 경우

4) this.forceUpdate로 강제로 렌더링을 트리거 할 경우


언마운트?

마운트의 반대 과정으로 DOM에서 컴포넌트를 제거하는 과정


관련 함수

1) render()

컴포넌트 모양새를 정의

이 함수 안에서 this.props, this.state에 접근

리액트 요소를 반환, 아무것도 보여주지 않는다면 null이나 false를 반환

state를 변형하면 안되고 웹 브라우저에 접근해서도 안됨 => DOM 정보를 가져오거나 변화를 줄 떄에는 componentDidMount에서 처리해야 함

2) Constructor()

Constructor(props) {...}

컴포넌트를 처음 만들 때 사용

초기 state설정

3) getDerivedStateFormProps()

리액트 v16.3 이후에 새로 만든 라이프사이클 함수

props로 받아온 값을 state에 동기화 시키는 용도로 사용

컴포넌트를 마운트하거나 props를 변경할 때 호출

4) componentDidMount()

componentDidMount(){...}

컴포넌트를 만들고 첫 렌더링을 마친 후 실행

다른 자바스크립트 라이브러리나 프레임워크의 함수 호출, 이벤트 등록, setTimeout, setInterval, 네트워크 요청 등 비동기 작업 처리

5) shouldComponentUpdate()

shouldComponentUpdate(nextProps, nextState) {...}

props또는 state 변경 시 리렌더링을 시작할지 여부를 지정하는 함수

반드시 true & false 반환 해야함

현재 props, state 변경은 this키워드를 붙이고 새로 설정할 props, state는 nextProps, nextState로 접근

  6) getSnapshotBeforeUpdate()

v16.3 이후 만든 메소드

ender 메소드를 호출한 후 DOM에 변화를 반영하기 바로 직전에 호출하는 함수

componentDidUpdate에서 세번 째 파라미터인 snapshot 값으로 전달 받을 수 있는데 주로 업데이트 하기 직전의 값을 참고할일이 있을 때

  7) componentDidUpdate()

componentDidUpdate(prevProps, prevState, snapshot) {...}

prevProps, prevState 사용하여 이전에 가졌던 데이터에 접근

getSnapshotBeforeUpdate에서 반환한 값이 있다면 여기서 snapshot 값을 전달 받을 수 있음

8) componentWillUnmount()

componentWillUnmount(){...}

DOM에서 제거할 때 실행

componentDidMount에서 등록한 이벤트, 타이머, 직접 생성한 DOM이 있다면 여기에서 제거 작업



예제

아래 예제는 함수가 언제 동작하는 지 확인해보기 위해 작성한 예제입니다.


색을 랜덤으로 업데이트 하는 버튼과 숫자를 올려주는 버튼 두개로 구성됩니다.


숫자가 4로 끝날 경우에만 리렌더링 되지않고, 나머지 숫자일 때에는 업데이트가 발생하면 리렌더링 됩니다.


LifeCylce.js

import React, { Component } from 'react';

class LifeCycle extends Component {
state ={
number : 0,
color : null
}
myRef = null;
constructor(props){
super(props);
console.log('constructor()');
}

static getDerivedStateFromProps(nextPorps, prevState){
if(nextPorps.color !== prevState.color){
return {
color:nextPorps.color
};
}
return null;
}
componentDidMount(){
console.log('componentDidMount()');
}
shouldComponentUpdate(nextPorps, nextState){
console.log("shouldComponentUpdate(),", nextPorps,",", nextState);
return nextState.number %10 !==4;
}

componentWillUnmount(){
console.log("componentWillUnmount()");
}

handleClick = () =>{
this.setState({
number : this.state.number +1
});
}

getSnapshotBeforeUpdate(prevProps, prevState){
console.log('getDerivedStateFromProps()');
if(prevProps.color !== this.props.color){
return this.myRef.style.color;
}
return null;
}

componentDidUpdate(prevProps, prevState, snapshot){
console.log('componentDidUpdate()');
if(snapshot){
console.log('업데이트 되기 직전 색상 : ',snapshot);
}
}

render() {
console.log('render()');
const style={
color:this.props.color
}
return (
<div>
<h1 style = {style} ref={ref=>this.myRef=ref}>
{this.state.number}
</h1>
<p>color : {this.state.color}</p>
<button onClick={this.handleClick}>plus</button>
</div>
);
}
}

export default LifeCycle;



app.js


import React, { Component } from 'react';
import './App.css';
import LifeCycle from './component/LifeCycle';

function getRandomColor(){
return '#'+Math.floor(Math.random()*16777215).toString(16);
}

class App extends Component {
state = {
color : '#000000'
}
handleClick = () =>{
this.setState({
color : getRandomColor()
});
}
render() {
return (
<div>
<button onClick={this.handleClick}>Random Color</button>
<LifeCycle color={this.state.color}/>
</div>
);
}
}

export default App;


동작결과


처음 렌더링 된 후 찍힌 결과입니다. 생성자 함수, getDerivedStateFromProps함수, render함수 componentDidMount 함수가 차례대로 실행 됩니다.



초기 렌더링 후 화면입니다.


랜덤 버튼과 플러스 버튼을 클릭했을 때의 결과 입니다. 숫자가 4로 끝나는 경우에만 리 렌더링 되지않고 나머지의 경우에는 리 렌더링 됩니다.


랜덤버튼 클릭


플러스 버튼 클릭


두 버튼을 클릭했을 때 바뀌는 화면입니다.



참고 : 리액트를 다루는 기술(저:VELOPERT)


'언어 > ReactJS' 카테고리의 다른 글

[React] 리덕스 개념  (0) 2019.05.24
[React] 함수형 컴포넌트  (0) 2019.05.21
[React] 컴포넌트 반복  (0) 2019.05.05
[React] Ref  (0) 2019.05.02
[React] 이벤트 핸들링  (2) 2019.04.25

컴포넌트 반복


리액트의 반복

JSTL의 <forEach>, AngularJS의 <ng-repeat>처럼 리액트에도 반복을 처리할 수 있는 방법이 있습니다.

리액트에서는 자바스크립트 배열 객체의 내장 함수인 map()함수를 이용합니다.


Map()

Map()

- 자바스크립트 배열 객체의 내장함수

- 파라미터로 전달된 함수를 사용해서 배열 내 각 요소를 프로세싱 한 후 그 결과를 새로운 배열로 생성해주는 함수

형태

arr.map(callback, [thisArg])

callback

      - currentValue: 현재 처리하고 있는 요소

      - index : 현재 처리하고있는 요소의 index

      - array : 현재 처리하고있는 원본 배열

thisArg(선택사항)

callback 함수 내부에서 사용할 this 레퍼런스

Key

-  리액트에서 컴포넌트 배열을 렌더링 했을 때 어떤 원소에 변동이 있는지 알아내려고 사용
- 단순히 출력만 하는 경우에는 Key가 필요하지 않지만, 게시판의 글을 수정, 삭제하는 등 고유 값이 필요한 경우에는 꼭 사용해야 합니다.
- props 설정과 유사


예제


아래 예제는 fruits 배열을 map함수를 이용해서 렌더링하는 예제입니다.


render() {
const fruits = ['사과','배','바나나','포도','수박'];
const fruitsList = fruits.map(
(fruit) => (<li>{fruit}</li>)
);

return (
<div>
<ul>
{fruitsList}
</ul>
</div>
);
}


동작 결과



정상적으로 렌더링 되지만, 아래와 같이 "key" prop이 없다는 경고가 뜨게 됩니다.



위의 예제에 Key를 추가하면 경고는 사라집니다.

index를 key로넘겨주면 index를 이용해서 쉽게 접근이 가능합니다.


const fruits = ['사과','배','바나나','포도','수박'];
const fruitsList = fruits.map(
(fruit,index) => (<li key={index}>{fruit}</li>)
);



참고 : 리액트를 다루는 기술(저:VELOPERT)




'언어 > ReactJS' 카테고리의 다른 글

[React] 함수형 컴포넌트  (0) 2019.05.21
[React] 라이프 사이클 함수  (0) 2019.05.07
[React] Ref  (0) 2019.05.02
[React] 이벤트 핸들링  (2) 2019.04.25
[React] State  (0) 2019.04.12

Ref


Ref(reference) ? 

HTML에서 id를 이용해서 DOM에 이름을 다는 것처럼 리액트 프로젝트 내부에서 DOM에 이름을 다는 개념입니다.


Ref를 사용하는 이유 ? 

ref는 DOM을 직접 건드려야 하는경우 사용합니다.

id를 사용할 수는 있지만, 같은 컴포넌트를 여러번 사용할 경우 유일해야하는 id가 중복이 발생 할 수 있습니다.


State로는 대체 불가능 한가?

아래와 같은 경우는 state만으로 해결이 불가능 합니다.

   1) 특정 <input>태그에 포커스를 주는 경우

   2) 스크롤 박스를 조작하는 경우

   3) <Canvas>태그에 그림을 그리는 경우


리액트에서 id는 사용 불가?

다른 라이브러리나 프레임워크를 사용할 경우 id를 꼭 사용해야만 하는 경우라면 중복 방지를 위해 뒤에 추가 텍스트를 붙여서 사용합니다.

예) button01, button02

 


Ref 사용 방법

props를 설정하는 방법과 유사하며 , ref 값으로 콜백 함수를 전달합니다.



<input ref="{(ref)=> {this.input.ref}}" />


Ref사용 예제(Scroll)


Ref를 이용해서 자식 컴포넌트인 ScrollBox의 스크롤을 아래, 위로 이동하는 예제입니다.


ScrollBox컴포넌트에서 <div>태그에 ref를 넘겨줍니다.


ScrollBox.js

import React, { Component } from 'react';

class ScrollBox extends Component {
scrollToChange =(param) =>{
const{scrollHeight, clientHeight} = this.box;
if(param==='d'){
this.box.scrollTop = scrollHeight - clientHeight;
}else{
this.box.scrollTop = 0;
}
}

render() {
const style={
border : '1px solid black'
, height : '300px'
, width : '300px'
, overflow : 'auto'
, position : 'relative'
};
const innerStyle ={
width : '100%'
,height : '500px'
,background : 'linear-gradient(white, black)'
}

return (
<div
style = {style}
ref={(ref)=>{this.box=ref}}>
<div style = {innerStyle}/>
</div>
);
}
}

export default ScrollBox;


ScrollBox에서 box를 넘겨받은 후 ScrollBox에서 정의한 함수 scrollToChange() 함수를 사용할 수 있습니다.


App.js

import React, { Component } from 'react';
import './App.css';
import ScrollBox from './component/ScrollBox';

class App extends Component {
state = {
upDown : 'd'
,value : 'To Bottom'
}
updownChange =() =>{
if(this.state.upDown === 'd'){
this.setState({
upDown:'u'
,value : 'To Top'
});
}else{
this.setState({
upDown:'d'
,value : 'To Bottom'
});
}
};
render() {
return (
<div>
<ScrollBox ref={(ref)=>this.ScrollBox=ref}/>
<button
onClick={
()=>{
this.ScrollBox.scrollToChange(this.state.upDown);
this.updownChange();
}
}
>
{this.state.value}
</button>
</div>
);
}
}

export default App;


동작 결과


참고 : 리액트를 다루는 기술(저:VELOPERT)





'언어 > ReactJS' 카테고리의 다른 글

[React] 라이프 사이클 함수  (0) 2019.05.07
[React] 컴포넌트 반복  (0) 2019.05.05
[React] 이벤트 핸들링  (2) 2019.04.25
[React] State  (0) 2019.04.12
[React] Props  (1) 2019.04.11

이벤트 핸들링


이벤트 핸들링 특징과 방법을 정리 했습니다.


자바스크립트나 제이쿼리보다 간편한 느낌이 들었습니다.



리액트 이벤트 핸들링 특징


1. 리액트에서 이벤트의 이름은 카멜 표기법으로 사용

예) onclick  onClick   onchange  onChange 


카멜 표기법으로 사용 하지않을 경우 아래와 같은 경고가 발생하고 정상적으로 동작하지 않습니다.


<input type = "text"
name = "message"
placeholder = "input message"
onchange={
(e) =>{
// console.log(e);
console.log(e.target.value);
this.setState({
message:e.target.value
})
}
}
value = {this.state.message}
/>



2. 이벤트에 실행할 코드를 전달하는것이 아닌 함수 형태의 객체를 전달


<button onClick={
()=>{
this.setState({
message : ''
})
}
}>


3. DOM 요소에만 이벤트 사용 가능


<div> <button> <input>등 DOM요소에만 이벤트를 사용할 수 있습니다.


직접 만든 컴포넌트에는 이벤트 사용이 불가합니다.


아래와 같이 직접 만든 컴포넌트에 이벤트를 작성하더라도 이벤트가 발생하지 않습니다.


render() {
return (
<EventPractice onLoad={
()=>{
console.log("test");
}
}/>
);
}


이벤트 종류


리액트에서 지원하는 이벤트는 아래 표와 같습니다.


Clipboard 

Form 

Composition 

Mouse 

Keyboard 

Selection 

Focus 

Touch

UI 

Image 

Wheel 

Animation 

Media

Transition 

 




이벤트 핸들링


1. 함수를 바로 작성하여 핸들링


함수 형태의 객체를 넘기기 때문에 함수형태로 작성해서 이벤트를 핸들링 합니다.


render() {
return (
<div>
<h1>이벤트 연습 - 함수를 랜더링 시 만들어서 핸들링</h1>
<button onClick={
()=>{
alert(this.state.message);
this.setState({
message : ''
})
}
}>
초기화
</button>
<input type = "text"
name = "message"
placeholder = "input message"
onChange={
(e) =>{
this.setState({
message:e.target.value
})
}
}
value = {this.state.message}
/>
</div>
);
}



2. 함수를 미리 작성하여 핸들링


미리 만들어 놓은 함수를 전달하여 핸들링도 가능합니다.


생성자 함수에서 만들어 놓은 함수를 바인딩 해줘야 합니다. 


constructor(props){
super(props);
this.handleChange = this.handleChange.bind(this);
this.handleClick = this.handleClick.bind(this);
}

handleChange(e){
let inputMessage = e.target.value;
console.log("input message : ",inputMessage);
this.setState({
message : inputMessage
})
}

handleClick(){
console.log("message == ",this.state.message);
this.setState({
message : ''
})
}


사용은 동일 합니다.


render() {
return (
<div>
<h1>이벤트 연습2 - 만들어논 함수를 전달해서 핸들링</h1>
<input type = "text"
name = "message"
placeholder = "input message"
onChange={this.handleChange }
value = {this.state.message}
/>
<button onClick={this.handleClick}>
초기화
</button>
</div>
);
}



3. Property Initializer Syntax를 사용한 함수 작성


생성자 함수를 사용하여 바인딩하는방법이 정석이지만,


새로운 함수를 추가할 때마다 생성자 함수를 수정해야하는 번거로움이 있습니다.


바벨의 transform-class-properties 문법을 사용하여 작성하면 

생성자 함수를 사용하지 않고 동적으로 바인딩이 가능합니다.


handleChange = (e) => {
let inputMessage = e.target.value;
console.log("input message : ",inputMessage);
this.setState({
message : inputMessage
})
}

handleClick = () => {
console.log("message == ",this.state.message);
this.setState({
message : ''
})
}



4. 동일한 여러개의 태그 핸들링


동일한 여러개의 태그는 event객체를 활용해서 처리 가능 합니다.


e.target.name은 태그의 name과 같습니다.


아래와 같이 두개의 <input>태그가 있을 때


render() {
return (
<div>
<h1>input 여러개 핸들링</h1>
<input type = "text"
name = "name"
placeholder = "input name"
onChange={this.handleChange }
value = {this.state.name}
/>
<input type = "number"
name = "age"
placeholder = "input message"
onChange={this.handleChange }
value = {this.state.age}
/>
<button onClick={this.handleClick}>
초기화
</button>
</div>
);
}


아래의 형태로 사용이 가능합니다.


[]를 쓰게되면 key가 됩니다.


state = {
name : ''
,age : 0
}


handleChange = (e) => {
console.log("e.target.name = ",e.target.name);
console.log("e.target.value = ",e.target.value);
this.setState({
[e.target.name] : e.target.value
})
}




5. onKeyPress 이벤트 핸들링


위의 예제에서 Enter키를 입력하면 초기화 버튼을 클릭하는 이벤트를 작성했습니다.


이벤트 처리를 위한 함수 작성


handleClick = () => {
alert("초기화!");
console.log("name = ",this.state.name, " age = ",this.state.age);
this.setState({
name : ''
,age : 0
})
}

handleKeyPress = (e) =>{
if(e.key === 'Enter'){
this.handleClick();
}
}


<input type = "number"
name = "age"
placeholder = "input message"
onChange={this.handleChange }
value = {this.state.age}
onKeyPress = {this.handleKeyPress}
/>



참고 : 리액트를 다루는 기술(저:VELOPERT)




'언어 > ReactJS' 카테고리의 다른 글

[React] 컴포넌트 반복  (0) 2019.05.05
[React] Ref  (0) 2019.05.02
[React] State  (0) 2019.04.12
[React] Props  (1) 2019.04.11
[React ]개발 환경  (1) 2019.04.08

State


State? 

유동적인 데이터를 다룰 때 사용하는 요소


State 특징

1) props는 부모 컴포넌트가 설정하기때문에 읽기 전용으로만 사용 가능합니다.


2) 컴포넌트 내부에서 사용하기 위해서는 state를 사용해야 합니다.



State 사용


1. state 값 지정


state 값을 지정하는 방법은 두 가지가 있습니다.


1) constructor 함수를 사용

constructor함수는 생성자함수 라고도 불리며 생성자 안에 state를 지정합니다.


import React, { Component } from 'react';

class StateComponent extends Component {
constructor(props){
super(props);
this.state = {
number : 100
}
}

render() {
return (
<div>
숫자 : {this.state.number}
</div>
);
}
}

export default StateComponent;


2) transform-class-properties 문법

바벨의 transform-class-properties 문법으로 사용하는 방법입니다. constructor함수보다 간편합니다.


constructor와 동시에 사용하면 constructor가 우선적으로 적용 됩니다.

 

import React, { Component } from 'react';

class StateComponent extends Component {
state = {
name : "HyeUng"
,number : 100
}

render() {
return (
<div>
숫자 : {this.state.number}
이름 : {this.state.name}
</div>
);
}
}

export default StateComponent;


2. state 값 업데이트


업데이트는 setState함수로 가능합니다.


아래 형태로 업데이트를 할 수 있습니다.


this.setState({

  수정 할 필드 이름 : 값

  , .... : ...

});


import React, { Component } from 'react';

class StateComponent extends Component {
state = {
name : "HyeUng"
,number : 100
}

render() {
return (
<div>
숫자 : {this.state.number}
<br/><br/>
<button onClick={()=>{
this.setState({
number : this.state.number - 1
})
}
}>빼기</button>
</div>
);
}
}

export default StateComponent;


실행결과


5번 클릭한 결과입니다.




참고 : 리액트를 다루는 기술(저:VELOPERT)


관련 포스팅

[React] State

[React] Props






'언어 > ReactJS' 카테고리의 다른 글

[React] 컴포넌트 반복  (0) 2019.05.05
[React] Ref  (0) 2019.05.02
[React] 이벤트 핸들링  (2) 2019.04.25
[React] Props  (1) 2019.04.11
[React ]개발 환경  (1) 2019.04.08

Props


Props?

prperties를 줄인 표현으로 컴포넌트 속성을 설정할 때 사용하는 요소


Props 특징

1) Props는 부모컴포넌트에서만 사용 가능합니다.

2) 변하지 않는 데이터를 다룰 때 사용합니다.



Props 렌더링 과정 

props 렌더링 → props 값 설정  props 기본 값 설정  props 값 검증


1. jsx 내부에서 props 렌더링


this키워드로 props에 접근 가능합니다.


PropComponent.js


import React, {Component} from 'react';
import PropTypes from 'prop-types';

class PropComponent extends Component{
render(){
return(
<div>
<p>안녕하세요, 제이름은 {this.props.name} 입니다.</p>
</div>
)
}

export default PropComponent;


app.js

import React, { Component } from 'react';
import './App.css';
import PropComponent from './component/PropComponent';

class App extends Component {
render() {
return (
<PropComponent name="hyeung"/>
);
}
}

export default App;


실행 결과





2. props 값 설정, 기본값 설정


defaultProps 객체를 이용해서 기본으로 값을 설정 가능하고, 별도 설정이 없다면 기본으로 설정된 값이 사용 됩니다.


값을 설정하는 방법은 두 가지가 있습니다.


1) 클래스 밖에서 설정



PropComponent.defaultProps= {
name : 'default name'
}


2) transform-class-properties 문법


class PropComponent extends Component{
static defaultProps = {
name : '기본 이름'
,age : 30
}
render(){
return(
<div>
<p>안녕하세요, 제이름은 {this.props.name} 입니다.</p>
<p>나이는 {this.props.age}살 입니다.</p>
</div>
)
}
}




3. props 값 검증


props 값 검증을 위해 타입을 지정할 수 있습니다.


1) props type 지정


type역시 값 설정과 같이 두가지 방법으로 설정이 가능합니다.


isRequired를 사용하면 필수값으로 지정이 가능합니다.


import React, {Component} from 'react';
import PropTypes from 'prop-types';

class PropComponent extends Component{
static defaultProps = {
name : '기본 이름'
,age : 30 // 제거 시 에러(isRequired)
}
static propTypes = {
name : PropTypes.arrayOf(PropTypes.string)
,age : PropTypes.number.isRequired
}
render(){
return(
<div>
<p>안녕하세요, 제이름은 {this.props.name} 입니다.</p>
<p>나이는 {this.props.age}살 입니다.</p>
</div>
)
}
}
PropComponent.defaultProps= {
name : 'default name'
}

PropComponent.propTypes = {
name : PropTypes.string
}

export default PropComponent;

 

2) props type 종류

- array : 배열

- bool  : true, false

- func  : 함수

- number : 숫자

- object : 객체

- string : 문자열

- symbol : ES6문법의 심볼 객체

- node   : 렌더링할 수 있는 모든 것(숫자, 문자열, element 또는 이들로 구성된 배열)

- element : 리액트 요소

- instanceof(MyClass) : 특정 클래스의 인스턴스

- oneOf(['male','Female']) : 주어진 배열 요소 중 값 하나

- oneOfType([React.PropTypes.string, React.PropTypes.number]) : 주어진 배열 안 요소중 하나

- arrayOf(React.PropTypes.number) : 주어진 종류로 구성된 배열

- objectOf(React.PropTypes.number) : 주어진 종류의 값을 가진 객체

- shape({name:React.PropTypes.string, age:React.PropTypes.number}) : 주어진 스키마를 가진 객체

- any : 아무 종류



3) 타입 관련 error


타입관련된 에러를 직접 발생 시켜 봤습니다.


name 타입을 잘못 입력하고, 

필수값인 age를 입력하지 않았습니다.


PropComponent.js


import React, {Component} from 'react';
import PropTypes from 'prop-types';

class PropComponent extends Component{
static defaultProps = {
// name : '기본 이름'
// ,age : 30 // 제거 시 에러(isRequired)
}
static propTypes = {
// name : PropTypes.arrayOf(PropTypes.string)
name : PropTypes.string
,age : PropTypes.number.isRequired
}
render(){
return(
<div>
<p>안녕하세요, 제이름은 {this.props.name} 입니다.</p>
<p>나이는 {this.props.age}살 입니다.</p>
</div>
)
}
}

export default PropComponent;

  

app.js

 
class App extends Component {
render() {
return (
<PropComponent name={3} />
);
}
}


페이지는 정상적으로 렌더링 되는 것처럼 보이지만




개발자 도구 콘솔창에는 에러가 발생 했습니다.


첫 번째는 string 타입인데 number 타입을 적용 한 경우

두 번째는 필수값을 입력하지 않아 undefined가 들어가서 발생한 경우 입니다.





관련 포스팅

[React] State

[React] Props




참고 : 리액트를 다루는 기술(저:VELOPERT)




'언어 > ReactJS' 카테고리의 다른 글

[React] 컴포넌트 반복  (0) 2019.05.05
[React] Ref  (0) 2019.05.02
[React] 이벤트 핸들링  (2) 2019.04.25
[React] State  (0) 2019.04.12
[React ]개발 환경  (1) 2019.04.08

React 개발환경



1. React?



React는 자바스크립트 라이브러리오직 View만 신경쓰는 라이브러리 입니다.


React는 DOM을 복사해서 Virtual DOM을 사용합니다.


변화가 생겼을 때 페이지 전체를 렌더링 하지않고, 변화한 부분만 실제 DOM에 적용하는 방식을 사용합니다. 




2. 개발 환경




1) Node.js / npm 설치


Babel, Webpack등을 사용하기 위해 NodeJS와 npm 설치가 필요합니다.


아래 URL에서 다운 가능합니다.


https://nodejs.org/ko/download/


설치를 완료하고 node -v 명령과 npm-v 명령으로 

노드와 npm 버전 확인이 가능합니다.




2) yarn 설치




npm은 의존하는 라이브러리 개수가 많으면 속도가 매우 저하 되기때문에 yarn 도구도 설치해줍니다.


아래 URL에서 windows용 다운 가능합니다.


https://yarnpkg.com/en/docs/install#windows-stable


yarn 역시 yarn -v명령으로 버전확인이 가능합니다.


해당 명령이 실행되지 않을 경우

 

yarn이 설치되어있는 경로 \bin폴더 까지 환경변수를 잡아주시면 됩니다.


3) VSCODE 설치



Visual Studio Code는 에디터 이지만, 


javascript 외에도 확장 프로그램을 설치하면 


C, Java, Spring boot 등 다양한 언어도 사용 가능해서 간편합니다.


아래 URL에서 설치 다운 가능 합니다.


https://code.visualstudio.com/download




3. 테스트 프로젝트 생성


1) create-react-app 도구 설치


npm, yarn 도구 둘다 설치 가능합니다.


저는 yarn을 이용해서 전역으로  설치했습니다.


터미널에 다음과 같이 입력해서 설치합니다.


> yarn global add create-react-app


2) 프로젝트 생성


create-react-app 도구를 이용해서 프로젝트를 생성합니다.


터미널에 다음과 같이 입력해서 설치합니다.


> create-react-app 프로젝트 명


프로젝트명으로 대문자는 사용이 불가합니다.


작업 환경에 따라서 1분~3분정도 소요됩니다.



3) 로컬 서버 실행


프로젝트가 생성되었으면, 해당 프로젝트로 이동 후


터미널창에 아래와 같이 입력하면 빌드후 로컬 서버가 실행됩니다.


로컬서버는 기본으로 3000번으로 열립니다.


파일이 수정될때마다 자동으로 다시 빌드하고 페이지를 리로드합니다.


> yarn start





참고 : 리액트를 다루는 기술(저:VELOPERT)

'언어 > ReactJS' 카테고리의 다른 글

[React] 컴포넌트 반복  (0) 2019.05.05
[React] Ref  (0) 2019.05.02
[React] 이벤트 핸들링  (2) 2019.04.25
[React] State  (0) 2019.04.12
[React] Props  (1) 2019.04.11

+ Recent posts