<input id="input-text-form" type="text" value="value" />
// in js
var textFormValue = document.getElementById('input-text-form').value;
규모가 커지면서 저러한 반복되는 코드들이 점점 늘어나고
만약에 라디오버튼 및 체크박스를 처리한다면? 매번 엘리먼트를 조회하고 체크가 되어있는지 해당 라디오가 선택되어있는지 검사를 해야한다.
그리고 이 값들을 통해 어디에 값을 조회하고 DOM 을 변경하고 한다면..?
생각만해도 복잡해지지 않을까?
- MVC 패턴을 프론트엔드에 녹이면서 이전에 과정하나하나 구현하는 부분이 줄어들고 역할이 명확해져 좋았으나, 양방향(Two-way Binding) 사고방식을 적용해서 독이되어버림.
- View 에서 사용자가 액션을 취하면 해당 액션이 컨트롤러에게 전달
- 해당 액션을 컨트롤러가 취해서 Model 에게 전달
- Model 은 해당하는 액션에 대한 로직 처리 및 data 를 업데이트
- Model 이 처리가 완료되면 컨트롤러에게 결과를 전달 컨트롤러는 전달된 결과를 View에 전달
- Model 은 처리가 완료되면 View 에게 결과를 전달 (Model 과 View 동기화)
-> 결과는 양방향 바인딩으로 인해 데이터가 어떻게 변경이 되었는가를 알기 어렵고 규모가 큰 애플리케이션에서는 Model 전부를 제어하는것이 어려움이 문제가 되었습니다. (구조도 복잡해집니다. + 만약 이를 줄이고자 하나의 Model 로 제어를 하면 단일책임원칙 위배 및 모델이 너무 커집니다)
- 이 구조의 우수한 점은 흐름이 한방향이라 추적이 쉽고, 요소 각각이 담당하는 역할이 단순하다
이 항목들에 대한 자세한 내용은 다루지 않겠다😷
- Action 정의
export const addTodo = () = ({
type : 'ADD_TODO',
payload : payload
})
// type 을 상수처럼 선언
const ADD_TODO = 'ADD_TODO';
export const addTodoAction = () = ({
type : ADD_TODO,
payload : payload
})
- addTodoAction 함수 역할은 그저 Action 에 해당하는 객체를 생성
Action 은 상태를 업데이트하는 유일한 방법이다. 업데이트 의도를 나타내므로 type 속성을 가진다. 단순 객체이므로 type 은 일반적인 문자열로 정의
전달할 데이터는 상태 업데이트에 필요한 것으로만 한정 하는 것이 정석
Action 을 전달하려면 dispatch() 를 사용
- Reducer 정의
const initialState = {
text : null
}
function addTodo(state = initialState, action) {
return state;
}
const initialState = {
text : null
}
export default function addTodo(state = initialState, action) {
switch(action.type) {
case 'ADD_TODO' :
return {
...state
}
default :
return state;
}
}
- combine reducer
import { combineReducers } from 'react-redux';
import addTodo from './todoModule/index.js';
export default combineReducers({
addTodo : addTodo
});
- create store
import {createStore} from 'redux';
import reducers from './combineReducers'
const store = createStore(reducers);
// package.json
{
"name": "react-redux-example",
"version": "0.1.0",
"private": true,
"dependencies": {
"@testing-library/jest-dom": "^4.2.4",
"@testing-library/react": "^9.5.0",
"@testing-library/user-event": "^7.2.1",
"react": "^16.13.1",
"react-dom": "^16.13.1",
"react-redux": "^7.2.0",
"react-scripts": "3.4.1",
"redux": "^4.0.5"
},
"scripts": {
"start": "react-scripts start",
"build": "react-scripts build",
"test": "react-scripts test",
"eject": "react-scripts eject"
},
"eslintConfig": {
"extends": "react-app"
},
"browserslist": {
"production": [
">0.2%",
"not dead",
"not op_mini all"
],
"development": [
"last 1 chrome version",
"last 1 firefox version",
"last 1 safari version"
]
}
}
다른분들 코드를 살펴보면👩💻 액션과 리듀서도 분리가 되어있지만, 저는 한 모듈에있는걸 선호
// /store/addCountModule/index.js
// action type
const ADD_COUNT = 'ADD_COUNT';
// action function
export const addCountAction = () => ({
type: ADD_COUNT
})
// state
const initialState = {
number: 0
};
// reducer
export default function addCount(state = initialState, action) {
switch (action.type) {
case ADD_COUNT:
return {
number: state.number + 1
}
default:
return state;
}
}
// /store/index.js
import { combineReducers } from 'redux';
import addCount from './addCountModule/index';
export default combineReducers({
addCount : addCount
})
// /store/store.js
import { createStore } from 'redux';
import reducers from './index';
const store = createStore(reducers);
export default store
// /src/index.js
import React from 'react';
import ReactDOM from 'react-dom';
import './index.css';
import App from './App';
// store
import store from './store/store';
import { Provider } from 'react-redux';
ReactDOM.render(
<React.StrictMode>
<Provider store={store}>
<App />
</Provider>
</React.StrictMode>,
document.getElementById('root')
);
<Provider></Provider>
를 이용하여 생성한 store 를 연결해준다.아 이제 설정 끝났다.. 그럼 어떻게 사용? 👨🏫
// app.js
import React from 'react';
import { useSelector, useDispatch } from 'react-redux';
import { addCountAction } from './store/addCountModule/index';
function App() {
const dispatch = useDispatch();
const number = useSelector((store) => store.addCount.number);
const addNumber = () => {
dispatch(addCountAction());
}
return (
<div>
redux test
<p>
{number}
</p>
<button onClick={addNumber}>add number</button>
</div>
);
}
export default App;