React만 잘하면 되는줄알았는데 Redux를 쓰면 상태관리하기 더 편하다고??
공부해보자. 정리안하면 머리에 남지않아서 나와같은 뉴비에게 도움이 되면 좋겠고, 나에게 특히 도움이 되기위해 Redux를 공부해보았다.
먼저 Redux에 대해 알아보고 추가로 React-Redux에 대해서도 알아보자
1. 컴포넌트가 많아질수록 더 이득이다.
React에서 컴포넌트를 생성하여 상태관리를 할때 가장 의문점이 컸었던것이 컴포넌트가 엄청많아지면 이 State들 관리 어떻게하지?? Props는? 어떻게함? 이라고 생각했었는데 Redux는 Store.js에서 State를 중앙집권 관리함으로써 이부분을 해결해준다
2. 버전관리가 가능하다
이부분이 진짜 놀란부분인데 확장 툴을 이용해서 동영상처럼 State의 변화흐름을 보여주고 그당시로 돌아갈수 있는 기능도 있더라. 참으로 꿀이아닐수가 없다.
이를 위해 State를 바로 변경하지 않고 복제하여 새로운 state를 만든다.
Object.assign 을 사용하거나 spread operator {...state} 를 사용하여 복제후 업데이트 한다.(하기 예시 참고)
if (action.type === 'INCREMENT') {
return { ...state, number: state.number + action.size };
}
아래의 전체 흐름을 이해하는것이 중요한것같다. 이후 각각의 단어들이 의미하는것을 알아보고 어떤역할을 하는지, 어떤방식으로 작동하는지 알아보자.
import { createStore } from 'redux';
export default createStore(function (state, action) {
return state;
//store 생성, Reducer 생성, reducer는 항상 state를 반환함//
}
createStore를 통해 스토어를 생성하여 State를 보관/관리한다.
// 초기 상태를 기록
console.log(store.getState());
// 상태가 바뀔때마다 기록
let unsubscribe = store.subscribe(() =>
console.log(store.getState())
)
Case 1
function todoApp(state, action) {
if (typeof state === 'undefined') {
return initialState }
// 지금은 아무 액션도 다루지 않고 // 주어진 상태를 그대로 반환합니다.
return state
}
Case 2
ES6 default arguments 문법을 이용해 더 간단하게 쓰는법
-> No value/ undefined값은 패스(무시된다)
function todoApp(state = initialState, action) {
return state
}
function todoApp(state = initialState, action) {
switch (action.type) {
case SET_VISIBILITY_FILTER:
return Object.assign({}, state, {
visibilityFilter: action.filter
});
default:
return state
}
}
위 코드는 현State를 받아 switch문을 활용/ State를 복제하여 Action에 따라 새로운 State를 반영하는 코드이다.
하기와 같이 Action을 인자로 가지고 action내에는 type(필수), 반환할 값(size)가 들어간다.
Type에는 어떤역할을 하는지 표기한다. 클래스명 처럼 알아먹기 좋게하기.
store.dispatch({ type: 'INCREMENT', size: size });
export function addTodo(text) {
return {
type: "ADD_TODO",
text
};
}
//text를 인자로하는 Action Creater//
dispatch(addTodo(text))
//액션을 담아서 dispatch 호출함//
//아니면 자동으로 액션을 보내주는 바인드된 액션 생산자를 만들수 있음//
const boundAddTodo = (text) => dispatch(addTodo(text))
//바로 호출가능//
boundAddTodo(text)
*Module : Action Type, Action creater, Reducer를 포함한 파일
/* 액션 타입 만들기 */
// Ducks 패턴을 따를땐 액션의 이름에 접두사를 넣어주세요.
// 이렇게 하면 다른 모듈과 액션 이름이 중복되는 것을 방지 할 수 있습니다.
const SET_DIFF = 'counter/SET_DIFF';
const INCREASE = 'counter/INCREASE';
const DECREASE = 'counter/DECREASE';
/* 액션 생성함수 만들기 */
// 액션 생성함수를 만들고 export 키워드를 사용해서 내보내주세요.
export const setDiff = diff => ({ type: SET_DIFF, diff });
export const increase = () => ({ type: INCREASE });
export const decrease = () => ({ type: DECREASE });
/* 초기 상태 선언 */
const initialState = {
number: 0,
diff: 1
};
/* 리듀서 선언 */
// 리듀서는 export default 로 내보내주세요.
export default function counter(state = initialState, action) {
switch (action.type) {
case SET_DIFF:
return {
...state,
diff: action.diff
};
case INCREASE:
return {
...state,
number: state.number + state.diff
};
case DECREASE:
return {
...state,
number: state.number - state.diff
};
default:
return state;
}
}
한 프로젝트에 여러개의 리듀서가 있는경우 Root reducer(합쳐진 Reducer)를 이용한다.
리듀서를 합치는 작업은 리덕스에 내장되어있는 combineReducers라는 함수를 사용합니다.
import { combineReducers } from 'redux';
import counter from './counter';
import todos from './todos';
const rootReducer = combineReducers({
counter,
todos
});
export default rootReducer;
import React from 'react';
import ReactDOM from 'react-dom';
import './index.css';
import App from './App';
import * as serviceWorker from './serviceWorker';
import { createStore } from 'redux';
import rootReducer from './modules';
const store = createStore(rootReducer); // 스토어를 만듭니다.
console.log(store.getState()); // 스토어의 상태를 확인해봅시다.
ReactDOM.render(<App />, document.getElementById('root'));
serviceWorker.unregister();
Redux야~ 편하게 해줘서 고마워~
나는 리액트 사용자이니 리액트에 적용하여 어떻게 사용할것인가에 대해 알아보았다.
Redux용 React 바인딩은 Presentational, Container 컴포넌트를 분리하여 사용하는것으로 채택했다. 두가지 컴포넌트의 특성부터 알아보자
Presentational 컴포넌트는 UI를 담당하며, Data 작업에는 관여하지 않는다.
Container 컴포넌트는 어떻게 동작할지 데이터 작업에 관여한다. React-Redux에서는 Connect()함수를 이용하여 Container를 생성한다.
Presentational 함수와 Redux를 연결해주기 위해 Container 함수를 만들어야 한다. 직접 작성할 수도 있지만 React redux 라이브러리에 내장된 Connect() 함수를 사용하여 Container 컴포넌트를 생성하는것을 공식문서에서 추천한다. --> 쓸데없는 렌더링을 막아주기 때문
위에서말한 Connect()를 사용하기 위해서는 2가지 함수를 정의하는 것이 필요하다.
const getVisibleTodos = (todos, filter) => {
switch (filter) {
case 'SHOW_COMPLETED':
return todos.filter(t => t.completed)
case 'SHOW_ACTIVE':
return todos.filter(t => !t.completed)
case 'SHOW_ALL':
default:
return todos
}
}
const mapStateToProps = state => {
return {
todos: getVisibleTodos(state.todos, state.visibilityFilter)
}
}
VisibleTodoList 컴포넌트는 todos를 필터링해서 TodoList에 넘겨주어야 하기 때문에, state.visibilityFilter에 따라 state.todos를 필터링하는 함수를 작성하고 이 함수를 mapStateToProps로서 사용할 수 있다.
2. mapDispatchToProps(): 액션 관련
이 함수를 통해 Store에 Action을 보낼 수 있다. Dispatch()를 인자로 받아 콜백으로 이루어진 속성들을 반환하면 presentational컴포넌트에 이 속성이 주입되게 된다.
const mapDispatchToProps = dispatch => {
return {
onTodoClick: id => {
dispatch(toggleTodo(id))
}
}
}
VisibleTodoList가 onTodoClick 속성을 TodoList에 주입하면서 onTodoClick 함수가 TOGGLE_TODO 액션을 파견하게끔 만들어주는 함수다.
앞서 mapStateToProps, mapDispatchToProps 함수를 생성했다면 이제 Connect()를 통해 Container(VisibleTodoList)를 생성한다.
import { connect } from 'react-redux'
const VisibleTodoList = connect(
mapStateToProps,
mapDispatchToProps
)(TodoList)
export default VisibleTodoList
import React from 'react';
import ReactDOM from 'react-dom';
import './index.css';
import App from './App';
import * as serviceWorker from './serviceWorker';
import {Provider} from 'react-redux';
import store from './store';
ReactDOM.render(
<Provider store={store}>
<App />
</Provider>
, document.getElementById('root'));
출처: https://react.vlpt.us/redux/04-make-modules.html![](https://velog.velcdn.com/images%2Fkms0206%2Fpost%2F6c913daf-d90c-414b-bbab-a0b0733fada2%2F%E1%84%89%E1%85%B3%E1%84%8F%E1%85%B3%E1%84%85%E1%85%B5%E1%86%AB%E1%84%89%E1%85%A3%E1%86%BA%202021-10-20%20%E1%84%8B%E1%85%A9%E1%84%92%E1%85%AE%206.03.15.png)
spread 연산자 : https://paperblock.tistory.com/62