동일한 데이터는 항상 같은 곳에서 가지고 온다.
즉, 스토어라는 하나 뿐인 데이터 공간이 있다는 의미이다.
리액트에서는 setState 메소드를 활용해야만 상태 변경이 가능하다.
리덕스에서도 액션이라는 객체를 통해서만 상태를 변경할 수 있다.
변경은 순수 함수로만 가능하다. 리듀서와 연관되는 개념이다.
Store(스토어) – Action(액션) – Reducer(리듀서)
단방향 데이터 흐름의 탄생
편리한 state 관리
액션은 무슨 일이 일어났는지 설명해주는 객체이다. 예를 들어 user라는 state를 수정해주는 역할을 하는 액션은 SET_USER
와 같이 선언하고, setUser(user)라는 함수 형태의 action creator을 만들어주면 된다.
액션 선언 (대부분 대문자로 선언)
const SET_WINNER = 'SET_WINNER';
const CLICK_CELL = 'CLICK_CELL';
const CHANGE_TURN = 'CHANGE_TURN';
액션을 반환하는 action creators (액션 생성 함수)
export const setWinner(user) {
return {type: SET_WINNER, user}
}
const reducer = (state = initState, action) => {
switch(action.type) {
case Action이름:
return {
...state,
바꿀 state: 지정 값
}
}
}
import { combineReducers } from 'redux';
import memberDetail from './memberDetail';
import member from './member';
export default combineReducers({
memberDetail,
member,
})
Action(액션)을 Store(스토어)에 바로 전달하는 것이 아니다.
Action(액션)을 Reducer(리듀서)에 전달해야 한다.
Reducer(리듀서)가 주문을 보고 Store(스토어)의 상태를 업데이트하는 것이다.
Action(액션)을 Reducer(리듀서)에 전달하기 위해서는 dispatch() 메소드를 사용해야 한다.
Store(스토어)는 상태가 관리되는 오직 하나의 공간이다.
컴포넌트와는 별개로 스토어라는 공간이 있어서 그 스토어 안에 앱 에서 필요한 상태를 담는다.
컴포넌트에서 상태 정보가 필요할 때 스토어에 접근한다.
import { createStore } from 'redux';
const store = createStore(rootReducer);
import { Provider } from 'react-redux';
import { createStore } from 'redux';
import rootReducer from './store/modules';
const store = createStore(rootReducer);
ReactDOM.render(
<React.StrictMode>
<Provider store={store}>
<App />
</Provider>
</React.StrictMode>,
document.getElementById('root')
);
reportWebVitals()
dispatch로 action을 발생 시킨다.
import { setMembersToStore } from '../../store/modules/member';
import { useDispatch } from 'react-redux';
const dispatch = useDispatch();
const saveMembersToStore = data => dispatch(setMembersToStore(data));
...
saveMembersToStore(member);
import { useSelector } from 'react-redux'
const user = useSelector(state => state.user);
import { change_user } from '../modules/user'
import { useDispatch } from 'react-redux'
const User = () => {
...
const dispatch = useDispatch();
dispatch(change_user(user));
...
}// 위에서 dispatch한 change_user는 아래와 같이 정의된 액션 생성 함수이다.
export const change_user = createAction(CHANGE_USER, user => user);
createAction을 사용하지 않았을 때
const CHANGE_USER = 'user/CHANGE_USER';
// 액션 생성 함수
export const change_user = user => ({type: CHANGE_USER, user});
createAction을 사용했을 때
import { createAction } from 'redux-actions';
const CHANGE_USER = 'user/CHANGE_USER';
// 액션 생성 함수
export const chage_user = createAction(CHANGE_USER, user => user);
handleAction을 사용하지 않았을 때
const reducer = (state = initState, action) => {
switch(action.type) {
case CHANGE_USER:
return {
... state,
user:action.user
}
}
}
handleAction을 사용했을 때
import { handleActions } from 'redux-actions';
const reducer = handleActions({
[CHANGE_USER]: (state, action) => ({...state, user: action.user})
});
import { createStore } from 'redux'
/**
* 이것이 (state, action) => state 형태의 순수 함수인 리듀서입니다.
* 리듀서는 액션이 어떻게 상태를 다음 상태로 변경하는지 서술합니다.
*
* 상태의 모양은 당신 마음대로입니다: 기본형(primitive)일수도, 배열일수도, 객체일수도,
* 심지어 Immutable.js 자료구조일수도 있습니다. 오직 중요한 점은 상태 객체를 변경해서는 안되며,
* 상태가 바뀐다면 새로운 객체를 반환해야 한다는 것입니다.
*
* 이 예제에서 우리는 `switch` 구문과 문자열을 썼지만,
* 여러분의 프로젝트에 맞게
* (함수 맵 같은) 다른 컨벤션을 따르셔도 좋습니다.
*/
function counter(state = 0, action) {
switch (action.type) {
case 'INCREMENT':
return state + 1
case 'DECREMENT':
return state - 1
default:
return state
}
}
// 앱의 상태를 보관하는 Redux 저장소를 만듭니다.
// API로는 { subscribe, dispatch, getState }가 있습니다.
let store = createStore(counter)
// subscribe()를 이용해 상태 변화에 따라 UI가 변경되게 할 수 있습니다.
// 보통은 subscribe()를 직접 사용하기보다는 뷰 바인딩 라이브러리(예를 들어 React Redux)를 사용합니다.
// 하지만 현재 상태를 localStorage에 영속적으로 저장할 때도 편리합니다.
store.subscribe(() => console.log(store.getState())))
// 내부 상태를 변경하는 유일한 방법은 액션을 보내는 것뿐입니다.
// 액션은 직렬화할수도, 로깅할수도, 저장할수도 있으며 나중에 재실행할수도 있습니다.
store.dispatch({ type: 'INCREMENT' })
// 1
store.dispatch({ type: 'INCREMENT' })
// 2
store.dispatch({ type: 'DECREMENT' })
// 1