1. 왜? 사용
- 소규모 프로젝트에서는 컴포넌트의 state 기능을 사용해도 충분함
-> 상태 객체가 너무 복잡하고 큼
-> 최상위 컴포넌트에서 상태관리를 하는 매서드를 너무 맣이 만들게됨
-> 하위 컴포넌트에 props를 전달하려면 여러 컴포넌트를 거쳐야 함
- store.subscribe: 함수를 스토어에 구독시킬때
- store.getState: 상태를 가져올때
* react-redux가 컴포넌트에서 리덕스를 구독하는 역할을 대신해줌
2. 이번 프로젝트는?
- 생서과 제거 버튼이 있음
- 생성을 누르면 원이 생김
- 원의 색상은 랜덤
- 마우스 좌,우 클릭을 통해 숫자와 색상을 변경
- 그렇다면 상태는 숫자와 색상 두개를 원 객체 마다 가지고 있지 않을까?
-> index, num, color
3. 라이브러리
- yarn add react-redux redux
4. 디렉터리 구조
1) 액션: 액션 타입과 액션 생성자 파일
2) 컴포넌트,: 뷰가 어떻게 생길지 담당하는 프레젼테이셔널 컴포넌트 모음
3) 컨테이너: 스토어에 있는 상테를 props로 받아오는 컨테이너 컴포넌트를 저장
4) 리듀서: 스토어의 기본 상태값, 상태의 업데이트를 담당하는 리듀서 파일들을 저장
5) 라이브러리: 이부 컴포넌트에서 함께 사용하는 파일 저장
5. 프리젠테이셔널 컴포넌트, & 컨테이너 컴포넌트
- 멍청한 컴포넌트와 똑똑한 컴포넌트로도 불림
6. 프리젠테이셔널 컴포넌트
- 오직 뷰만 담당
- DOM 엘리먼트와 스타일이 있을 수 있음
- 프리젠테이션 컴포넌트와 컨테이너 컴포넌트가 있을 수 있음
- 리덕스에 직접 접근할 권한이 없음
- 오직 props로만 데이터를 가지고 옴
- 대부분은 state가 없음, 있다고 해도 UI에 관련된 것임
- 주로 함수형 컴포넌트로 작성
7. 컨테이너 컴포넌트
- 프리젠테이션 컴포넌트와 컨테이너 컴포넌트를 관리 담당
- 내부에 직접적으로 DOM 엘리먼트를 사용 안함
- 스타일도 없음
- 상태를 가지고 있을때가 많음, 리덕스에 직접 접근할 수 있음
8. Counter 컴포넌트
- 숫자와 색상값, 더하기, 배깨, 색상변경 함수를 props로 전달 받음
- 마우스 좌클릭: 더하기
- 마우스 우클릭: 빼기
- 마우스 더블클릭: 색상 변경하기
* e.preventDefault()을 우클릭에 사용하는 이유? 메뉴가 열리는 것을 방지
9. import PropType from 'prop-types';
Counter.propTypes = { number: PropTypes.number, ... }
-> 이거 해당 값의 타입을 만족 시키기 위해서 정의해 놓은 거 같음.
10. 리덕스 > 액션생성
- 액션은 객체이다.
-> 객체(오브젝트), 인스턴스, 클래스 차이점:
-> 클래스가 설계도이고 인스턴스는 찍어서 나온거고 객체는 그냥 ?? 머였지? 오브젝트는 컨셉이다. 만들기 전이다.
- 모든 액션 객체에는 type 값이 필수로 있어야함
{ type: "SET_COLOR" }
- 추가할 사항이 있으면 다음과 같이 함
{ type: "SET_COLOR", color: "black" }
- type은 액션 이름과도 같음
- 리듀서가 이값을 전달 받아서 실행해야함
- 이 값들은 따로 만들어서 관리하면 좋음
11. ACtionTypes 준비
- ActionTypes.js
export const INCREMENT = 'INCREMENT';
-> 앞에 export를 붙이면 import * as types from './ActionTypes'
- 액션을 선언할때는 대문자로 선언
12. 액션 생성 함수 만들기
- 액션을 만들때마다 객체를 만드는 것은 번거롭다.
- 액션을 만들어내는 함수를 만들자.
() => ( { } ) 화살표 함수는 function() { return{ } } 함수와 동일
- index.js > import * as types from './ActionTypes'
- export const increment = () => ({ type: types.INCREMENT })
-> 이게 왜 필요한지 곰곰히 생각해 보았다.: 자동완성 때문인가?
- 파라미터를 가지고 있는 값 표현
- export const setColor = (color) => ({ type: types.SET_COLOR, color})
- 리듀서는 state와 action을 파라미터로 가지고 있음
- 리듀서 함수 내부에서 action.type에 따라 상태에 다른 변화를 일으킴
- state를 직접 변형하면 안됨
- state 값에 새 상태 객체를 만드는 방식으로 진행해야함
* 이해 한 부분 정리
1. 리듀서 파라미터는 상태와 액션을 보낸다.
2. 상태가 undefined면 initialState를 값을 갖는다.
3. 액션 타입에 따라 switch로 함수 호출
4. 상태가 변환 경우 기존 값에 덮어 쓰기 안하고 새 상태를 만들어서 진행
5. 리듀서는 액션을 리덕스에서 받아서 스토어의 변형을 일으키기 위해서 필요함
function counter (state = initialState, action){
switch(action.type){
case types.INCREMENT:
return {
... state,
number = number + 1
};
}
}
14. 스토어 생성
- 스토어는 리덕스에서 가장 핵심적인 인스턴스임
- 현재 상태가 내장됨
- 상태를 업데이트 할때마다 구독 중인 함수를 호출
- 프로젝트의 엔트리 포인트 index.js
- 리덕스 관련 불러오기
-> import { createStore } from 'redux';
-> import reducer from './reducers';
- 스토어 생성
-> const store = createStore(reducers);
- 다음단계: 이 스토어를 리액트 컴포넌트로 전달
15. Provider 컴포넌트로 리액트 앱에 store 연동
- Provider : react-redux 라이브러리에 내장된 리액트 어플리케이션에 손쉽게 스토어를 연동할수
있도록 도와주는 컴포넌트임
- import { Provider } from ' react-redux '
- ReactDOM.redner(
< Provider store={ store} >
<APP/>
</Provider>,
document.getElementById('root')
);
16. 컨테이너 컴포넌트
- 스토어가 연동됨
- 컨테이너 컴포넌트와 스토어를 연결
- react-redux 라이브러리의 connect 함수를 이용해서 연결
- connect 함수는 파라미터가 3개
-> mapStateToProps
-> mapDispatchToProps
-> mergeProps
- 파라미터는 함수형태임,
- 꼭 필요한 것은 아님
- 컴포넌트에서 사용할 props를 반환
1) mapStateToProps: store.getState() 결과값인 state를 파라미터로 받아 컴포넌트의 props로 사용할 객체를 반환: state에서 props?
2) mapDispatchToProps: dispatch를 파라미터로 받아 액션을 디스패치하는 액션을 디스패치하는 함수들을 안에 넣어서 반환 -> 디스패치에서 함수?
3) mergeProps: state와 dispatch가 동시에 필요한 함수를 props로 전달하려고 할때 사용 -> 일반적으로 잘 사용하지 않음
- connect 함수를 호출하고 나면 또 다른 함수를 반환
-> 이때 반환하는 함수의 파라미터로 리덕스에 연결시킬 컴포넌트를 넣으면 mapStateToProps와 mapdispatchToProps에 정의한 값을 props로 받아오는 새 컴포넌트를 만듬
- store 안에 state 값을 props 값에 연결
-> const mapStateToProps = (state ) => ({
color : state.color,
number: state.number
});
- 액션 생성함수를 이용하여 액션을 생성,
- 해당 액션을 dispatch하는 함수를 만든 후 이를 props로 연결
-> const mapDispatchToProps = (dispatch) => ({
on increment = dispatch( actions.increment())
})
* actions -> import * as actions from './actions'
- counter 컴포넌트의 container 컴포넌트
- counter 컴포넌트를 애플리케이션 데이터와 묶는 역할을 함
const container = connect(
mapStateToPros,
mapDispatchToProps
)(Counter);
-> 이렇게 하면 counter 컴포넌트의 props로 들어간다.
-> 이해한 부분을 정리해 보자.
상태를 만들고
-> 액션을 만들기: 정의수준
-> 리듀서 만들어서 액션에 대응해서 행동을 정의
-> 디스패치되어서 액션에 변화를 주게됨
-> 구독한 함수에게 영향을 미침
}
* 일단 이렇게 마든다고 함
const reducers = combineReducers({
numberData: number,
colorData: color
})
-> const mapStateToProps = (state ) => ({
color : state.ColorData.color,
number: state.numberData.number
});
18. 크롬 개발자 도구 툴: redux dev tools
- 스토어를 생성하는 코드를 수정
-> const store = createStore( reducers, window.divToolsExtensioin && window.devToolsExtension());
- 현재 리덕스 상태가 어떤지
- 방금 디스패치한 액션은 무엇인지
- 액션으로 어떤값을 바꾸었는지
19. Actions 수정
- create, remove 버튼 생성
- 액션 생성함수 수정: 특정 카운터를 조작할 수 있도록 수정 index 값을 액션 객체에 포함
export const increment = (index) =>({
type: types.INCREMENT,
index
})
- increment(3) 이면 index가 3인 카운터값을 1씩 올린다.
20. 리듀서 수정
- const initialState = { counters = [ { color: black, number:0} ] };
- 생성: 기존 배열 가지고 추가로 하나 더 생성 , 전개 연산자(...)를 사용하거나 slice 함수로 배열을 잘라서 새로 생성해야함
- 삭제: 끝에서 하나 지움
switch (action.Type){
(...)
case types.INCREMENT:
return {
counter: [
... counters.slice(0, action.index), -> 선택한 인덱스의 전 아이텐들
{
...counters[action.index], -> 기존 객체에
number: counters[action.index].number + 1 -> 새 number 값 덮어쓰기
},
... counters.slice(action.index+1, actioni.index.length) -> 선택한 index의 다음 아이템들
]
}
}
21. slice 함수
- .slice()는 배열의 일부분을 선택하여 새로운 배열을 만듭니다.
22. counterList > counter
- counter: 화면과 액션이 어울려져 있는 곳
- counterListContainer
-> mapStateToProps: 리덕스 스토어에 있는 값들을 props로 전달
-> mapDispatchToProps: 액션 생성 함수들을 연결

"파트너스 활동을 통해 일정액의 수수료를 제공받을 수 있음"
'프로그래밍' 카테고리의 다른 글
[리액트를 다루는 기술] 20. blog 실습 (0) | 2018.10.18 |
---|---|
[리액트를 다루는 기술] 15. 리덕스 미들웨어와 외부 데이터 연동 (0) | 2018.10.13 |
[리액트를 다루는 기술] 12. 리덕스 개념이해 (0) | 2018.10.09 |
[리액트를 다루는 기술] 11. 컴포넌트 리랜더링 최적화 (0) | 2018.10.09 |
[리액트를 다루는 기술] 10. 일정관리 (0) | 2018.10.09 |