05 Feb 2021
|
React
React.js Redux
-
Redux 개요
- Redux는 애플리케이션 state를 관리하기 위한 오픈소스 Javascript 라이브러리이다
- Redux는 Javascript App을 위한 예측가능한 state container이다
- Redux는 React의 작동 원리인 state에 대한 변화를 쉽게 예측할 수 있도록 도와준다
- React 뿐만 아니라 Angular, jQuery, Vanilla JS 등 다양한 곳에서 적용이 가능하다
- 앱의 규모가 커질수록 local state를 props의 형태로 전달하기 위해서는 연결된 chain layer들을 모두 거쳐야 한다
- 위와 같은 경우 Context API를 쓸 수도 있다. 다시 말해, Redux만이 유일한 수단은 아니다
- Redux의 3 원칙
- Single source of truth : global state는 트리 구조에서 하나의 저장소에 저장된다
- State is read-only : state를 변경하는 유일한 방법은 action을 발생시키는 것이다
- Changes are made with pure functions : action에 의한 state tree 변환을 지정하려면 pure reducer를 작성해야 한다. 즉, 리듀서는 action과 state를 받아서 다음 state를 반환하는 순수 함수여야한다

출처 : Udemy / React - The Complete Guide
-
Redux 기본 사용법
- npm install –save redux (redux 패키지)
- npm install –save react-redux (react와 redux를 연결)
- Provider는 리액트의 컴포넌트들이 Redux store에 접근할 수 있도록 연결해준다 (상위 컴포넌트에서 사용)
- store는 애플리케이션의 state를 저장하는 객체이다
import { createStore } from 'redux';
import { Provider } from 'react-redux';
import reducer from 'path/reducer';
// store 생성, reducer를 넣어준다
const store = createStore(reducer);
ReactDOM.render(
// 하위 컴포넌트를 Provider로 감싸고 store 연결
<Provider store={store}>
<App />
</Provider>
document.getElementById('root')
);
- reducer는 현재 state와 action을 취하고 새로운 state를 반환하는 순수 함수이다. state는 불변성을 유지해야한다. 즉, state의 직접 수정 없이 복사본을 만들어 수정해야한다. 그 이유는 redux의 변경 감지 알고리즘에 있는데, 자세한 것은 링크 참조.
const initialState = {
counter: 0,
};
// ES6 default parameter, state가 undefined일 경우 initialState로 초기화
const reducer = (state = initialState, action) => {
if(action.type === 'INCREMENT') {
return {
counter: state.counter + 1,
}
}
return state;
};
export default reducer;
- action은 일반 객체로 state의 변화가 필요할 때, action을 발생시킨다. type 필드를 필수적으로 가지고 있어야한다. 일반 객체이기 때문에 얼마든지 함수로 생성하여 사용할 수 있다.
- dispatch는 store의 내장 메소드로 action을 파라미터로 받아 store의 reducer에 넘겨주는 역할을 한다.
- connect() 메소드는 Provider 컴포넌트로 감싼 하위 컴포넌트들이 store에 접근하게 하는데 하위 컴포넌트에서 사용한다
- mapStateToProps : connect 메소드의 첫번째 인자로 들어가는 함수, store의 state를 조회해서 props로 넣어준다. 파라미터로 state를 받아온다. null 값으로 자동 호출을 막을 수 있다.
- mapDispatchToProps : connect 메소드의 두번째 인자로 action을 dispatch하는 함수를 만들어 props로 넣어준다, dispatch를 파라미터로 받는다.
- 이름은 꼭 저게 아니어도 되지만 일반적으로 통용되는 것으로 맞춰주는게 좋다
import React from 'react';
import { connect } from 'react-redux';
class Counter extends React.Component {
redner() {
return (
<button onClick={this.props.onIncrementCounter}>
{this.props.ctr}
</button>
);
}
}
const mapStateToProps = state => {
return {
ctr: state.counter,
};
}
const mapDispatchToProps = dispatch => {
return {
onIncrementCounter: () => dispatch({ type: 'INCREMENT'}),
};
}
export default connect(mapStateToProps,mapDispatchToProps)(Counter);
-
payload
- 위 코드에서는 reducer의 action 실행문에 의해 state의 값이 1씩 증가하게 된다. 하지만, 저렇게 하드코딩 되어있는 부분을 사용자가 지정한 임의의 값이 증가되도록 하려면 어떻게 해야될까?
- action은 일반 객체라고 했다. action을 dispatch할 때, 단순히 type 외의 다른 필드 값을 넘겨주고 reducer에서 받아서 사용하기만 하면 된다
const mapDispatchToProps = dispatch => {
return {
onIncrementCounter: () => dispatch({ type: 'INCREMENT', value: 5}),
};
}
const reducer = (state = initialState, action) => {
if(action.type === 'INCREMENT') {
return {
counter: state.counter + action.value,
}
}
return state;
};
- 저렇게 해도 작동은 잘 된다. 하지만, type 이외의 전달하고 싶은 값들은 되도록 payload 필드명을 이용하여 전달하는 것이 좋다. 그렇게 통일되어 있으니깐. createAction 메소드를 이용하여 action을 생성할 때도 두 번째 인자값으로 paylaod 값을 받도록 설정되어 있다.
-
Updating State Immutably
- Redux의 핵심 규칙 중 하나는, state는 read-only이기에 불변성(immutable)을 유지해야 한다고 했다.
- 위의 코드들과 달리 state가 복수개 존재한다면 기존 state를 통으로 복사해 필요한 부분만 수정하면 될 것이다.
- 여기서 ES6의 Object Spread Operator가 사용된다. Object.assign()으로 객체 복사를 해도 된다.
const initialState = {
counter: 0,
result: [],
};
const reducer = (state = initialState, action) => {
if(action.type === 'INCREMENT') {
return {
...state, // 객체 복사
counter: state.counter + action.value,
}
}
if(action.type === 'STORE') {
return {
...state, // 객체 복사
results: state.results.concat({
// id는 임시 id
id: new Date(), value: state.counter }),
}// 기존배열을 직접 수정하지 않기 위해 concat 사용
}
return state;
};
export default reducer;
-
Constant Action Type
- 위의 코드들은 action을 하드코딩해서 직접 써넣었다
- 이것을 상수로 설정하여 오타 등 에러가 날 확률을 미연에 방지할 수 있다
export const INCREMENT = 'INCREMENT';
export const DECREMENT = 'DECREMENT';
// 상수가 담긴 js 파일, 사용처에서 import하여 사용한다
import * as actionTypes from 'path/actions';
const reducer = (state = initialState, action) => {
if(action.type === actionTypes.INCREMENT) { // 상수 사용
return {
...state, // 객체 복사
counter: state.counter + action.value,
}
}
import * as actionTypes from 'path/actions';
const mapDispatchToProps = dispatch => {
return {
onIncrementCounter: () => dispatch({ type: actionTypes.INCREMENT, value: 5}),
};
}
-
Combining Multiple Reducers
- 애플리케이션의 규모가 커짐에 따라 한 개의 리듀로서만 모든걸 다루기에는 한계가 있다
- 리듀서를 쪼개서 하나의 리듀서처럼 사용할 수 있다
- 이를 위해서는 redux 패키지의 combineReducers가 필요하다
// store를 정의한 최상위 컴포넌트에서
import { createStore, combineReducers } from 'redux';
import counterReducer from 'path/counter';
import resultReducer from 'path/result';
const rootReducer = combineReducers({
ctr: counterReducer,
res: resultReducer,
});
const store = createStore(rootReducer);
const mapStateToProps = (state) => {
return {
// state의 블럭 단위가 한 단계씩 늘어난다
ctr: state.ctr.counter,
storedResults: state.res.results,
};
};
- reducer를 쪼개면 그에 따라 state도 나뉘게 된다. 자신에게 없는 state 값이 필요한 경우엔 어떻게 해야할까?
- 리듀서는 함수 안에서 global state에 접근할 수 없기때문에 값을 받아와야한다
- 하나의 방법으로 action을 이용하여 값을 전달할 수 있다
<button onClick={() => this.props.onStoreResult(this.props.ctr)}></button>
const mapDispatchToProps = (dispatch) => {
return {
onStoreResult: (result) => dispatch({
type: actionTypes.STORE, result: result })
}// action을 이용하여 state를 전달
}
const reducer = (state = initialState, action) => {
if (action.type === actionType.STORE) {
return {
...state,
results: state.results.concat({
id: new Date(),
value: action.result,
}),
};
}
}
참고 자료
reactjs.org - 공식홈페이지
Udemy - React The Complete Guide
redux 공식 홈페이지
React에 Redux 적용하기
리덕스(Redux)의 리듀서(reducer)가 순수 함수여야만 하는 이유
Redux-React의 기본
05 Feb 2021
|
로그포스
2021-02-05 TIL
-
오늘 한 것
-
리액트 공부 Redux - 리덕스의 기초적인 개념을 배웠는데 아직은 감이 잘 오지 않는다. 이론적인 부분을 다 나가고 실제로 사용해보면서 익혀야겠다.
-
학원 비대면 수업(15:30~22:00) HTML - 학원 진도가 늦어 초스피드로 하루 만에 HTML을 끝내버렸다. 자료가 옛날 자료라 지금 웹표준에 맞지 않는 부분도 많은데 그래도 다시 한 번 HTML을 정리해 볼 수 있었다.
- 내일 할 것
- 리액트 공부 - React The complete Guide
학원 커리큘럼에 자바스크립트도 포함되어 있지만 이틀 배우고 끝이다. 메인 언어가 자바라서 아쉽다. 자바스크립트를 주 언어로 사용하고 싶은지라 이쪽 생태계의 라이브러리나 프레임워크는 내가 알아서 공부해야지.
오늘의 한 줄 총평 : 수박 겉 핥기..
04 Feb 2021
|
로그포스
2021-02-04 TIL
- 오늘 한 것
- 학원 비대면 수업(15:30~22:00) 개인 프로젝트 발표 - 비대면으로 발표를 하는 진귀한(?) 경험을 했다. 화면 공유해서 마이크로 설명했다. 다른 친구들이 만든 프로젝트를 유심히 봤는데 잘하는 친구들은 정말 잘했는데 그 반대인 친구들이 많았다. 안타깝게도 아예 완성조차 못한 친구들도 많았다. 수료할때쯤이면 팀을 꾸려서 팀 프로젝트도 하게 될텐데 이번에 눈여겨본 친구들과 같이 팀을 짜서 서로 시너지가 발휘되었으면 좋겠다.
- 내일 할 것
- 리액트 공부 - React The complete Guide
- 학원 비대면 수업(15:30~22:00) HTML
다행히 발표를 무사히 마쳤지만 절대 자만하지 않고 그냥 원래 하던대로 내 하루 공부량 채우면서 꾸준히 가자.
오늘의 한 줄 총평 : 개인 프로젝트 종료