상태 관리를 도와주는 라이브러리이다.
상위 컴포넌트의 State를 하위 컴포넌트로 전달 하고, 또 그 전달받은 하위컴포넌트가 그 State를 또 자신의 하위 컴포넌트에게 전달하고.. 이런식으로 계속 전달하다보면 나중엔 엄청 복잡해져 있을것이다. 리덕스는 State를 전역으로 관리할 수 있게 도와주는 라이브러리다!
Action : 어떤 일이 발생했는가 설명하는 객체
{ type : 'LIKE_ARTICLE', articleId : 42 },
{ type : 'FETCH_USER_SUCCESS', response: { id: 3, name: 'Mary'},
{ type : 'ADD_TODO', text : 'Read the Redux docs' }
Reducer : 이전 State 와 Action Object를 받은 후에 Next State를 Return 한다.
(previousState, action) => nextState
Store : 전체적인 어플리케이션의 State을 감싸주는 역할, 스토어 안에는 여러가지의 많은 메소드들이 있고, 그 메소스들을 이용하여서 State을 관리할 수 있음.
받아야 할 패키지가 4가지 정도 있다.
1. redux
2. react-redux
3. redux-promise
4. redux-thunk
npm install redux react-redux redux-promise redux-thunk --save
여기서 redux-promise 와 redux-thunk는 리덕스를 좀 더 잘 활용할 수 있게 하려고 다운받음!
리덕스 스토어는 오직 객체로 이루어진 액션을 dispatch 하는 것을 받아들여야하는데, Promise 형식으로 받거나, Function 형태로 받을 때 가 있다.
그러면 이 경우에는 액션을 dispatch 할 수 없게 되는것이다..
그래서 Promise 나 Function을 리덕스 스토어가 어떻게 처리해야할 지 도와주는 패키지가 redux-promise 와 redux-thunk 이다.
초기 설정은 아래 사진처럼 시작한다.
👇👇👇👇👇👇👇👇👇👇👇👇👇👇👇👇
React-redux 패키지에 있는 Provider 컴포넌트로, 우리의 App을 감싸준다.
그 다음 우리는 스토어를 생성하고, 그 스토어를 우리 Provider에 넣어줘야한다.
그 다음은 reducer를 관리하는 폴더를 만들고, index.js 파일을 생성하여, 루트 리듀서를 만든다. 아직은 다른 리듀서가 없지만, 보통은 이 루트 리듀서에서 combineReducers 함수를 사용하여, 각각 다른 리듀서를 한방에 묶어서 처리한다!
코드의 흐름을 적어보면서, 이해해보기
dispatch(loginUser(body)); // loginUser 디스패치!
1. 어떤 곳에서, loginUser 액션 생성 함수를 디스패치
디스패치 : 액션을 발생시키는 것을 말한다.
이 함수는 dispatch(action)과 같은 형태로 액션객체를 파라미터로 넣어서 호출
이 함수가 호출되면 스토어는 리듀서 함수를 실행시켜서 새로운 상태를 만들어준다.
export const loginUser = (dataToSubmit) => {
// post method로 "api/user/login" 데이터와 함께 요청 보내기!
const request = axios // payload
.post("/api/users/login", dataToSubmit)
.then((response) => response.data);
return { // loginUser 함수는 액션을 반환하고, 반환된 액션을 dispatch
type: LOGIN_USER,
payload: request,
};
};
return 부분을 주목!
액션은 타입과 페이로드가 있는 객체를 의미함! loginUser는 액션 생성 함수 (액션을 만들어주는 함수)
2. 리듀서에서 디스패치가 발생한 액션을 감지하고 액션을 처리함.
const userReducer = (state = {}, action) => {
switch (action.type) {
case LOGIN_USER:
return { ...state, loginSuccess: action.payload };
default:
return state;
}
};
위 코드를 보면 case에 명시되어있는 LOGIN_USER는 액션이 가지고 있는 타입과 똑같다!
고로 이 리듀서는, LOGIN_USER라는 타입의 액션을 처-리 해줌!
어떻게 처리하는지 예를 들어보면, case 문에 명시되어있는 액션타입이 dispatch 되면 이 리듀서에 알람이 옴! 그 특정한 액션이 발생했다고! 그러면 이 리듀서에서 새로운 State를 반환해줌!
이 코드같은경우에는, 아까 우리 LOGIN_USER 액션이 들고 있던 소지품 기억남?
그 소지품을 State의 loginSuccess 값에 넣은거임!
그러면 어떻게 될까요! 우리 어플리케이션의 state에는 loginSuccess : request
뭐 이렇게 되있지 않을까?
근데 여기서 의문, 아니.. userReducer가 그냥 저렇게 선언만 해놓는데 액션 감지는 어떻게 하는거임?
맞다. 코드를 어느정도 짜봤으면 당연히 이해가 안된다! 아니 저렇게 선언만 해놨는데 실행되서 알아서 일을 처리하는게 말이되나?!
import { combineReducers } from "redux";
import user from "./user_reducer";
// 리듀서는 여러가지가 있을 수 있는데, combineReducers 함수는 그 여러가지
// 리듀서들을 하나로 합쳐주는 역할
// import comment from "./comment_reducer";
const rootReducer = combineReducers({
user,
});
export default rootReducer;
여기 보이는 rootReducer가 여기서는 저 리듀서들을 담는 바구니 역할을 함!
그 이유는 combineReducers 때문인데, 이 친구는 리듀서를 여러개 담을 수 있는 바구니같은 존재임!
만약 이 친구가 없었으면 rootReducer 말고 위에 있었던 userReducer 이런거를 따로따로 store에 넣어야 했었을거임!
아무튼 이 바구니에 필요한 리듀서를 몽 땅 담는다!
그 다음 스토어에 이 리듀서들을 넣어주는거임!! 그래야 리듀서들이 영혼이 생김!! 그래서 선언만 해놔도 액션을 디스패치하면, 각각 맞는 리듀서들이 일을 하기 시작하는것이다!
ReactDOM.render(
<Provider
store={createStoreWithMiddleware(
rootReducer,
// Redux_devtools 크롬 확장앱을 사용하기 위해 넣어준 코드
window.__REDUX_DEVTOOLS_EXTENSION__ &&
window.__REDUX_DEVTOOLS_EXTENSION__()
)}
>
<App />
</Provider>,
document.getElementById("root")
이 파일은 우리가 CRA로 리액트 앱을 만들었을때 생기는 index.js임!
여기 보이면 Provider가 우리의 앱을 감싸고있는것이 보일것임!
그리고 store props에 우리의 rootReducer가 있음.
이제 느낌이 오지않습니까 허허~
Provider 컴포넌트가 우리의 앱을 감싸고있고, store props가 있는데, 그 안에 rootReducer가 들어가있다??????? 이거이거?!
(너무 복잡하군요.. 리덕스)