- 23-12-09 : #0.1 ~ #0.4 / Set up
- Redux
- JavaScript App들의 state를 관리하는 방법을 가지는 라이브러리
- React, Vue, Angular, VanillaJS 등 가능
- 설치법 :
npm i redux
- JavaScript App들의 state를 관리하는 방법을 가지는 라이브러리
- Redux
- 23-12-13 : #1.0 ~ #4.3 / createStore + react-redux + Redux Toolkit(1)
- createStore
- state를 넣을 수 있는 장소를 생성
- 기본형 :
const 스토어명 = createStore(리듀서);
- 리듀서 : state를 수정하는 함수(modifier)이며, state를 인자로 받을 수 있음
- 리듀서 기본형 :
const 리듀서명 = (state = 기본값) => { ... };
- 내부 코드는 if-else문 보다 switch문 사용을 권장
- 리듀서 기본형 :
- ex.
const countModifier = (count = 0) => { console.log(count); return count; }; const countStore = createStore(countModifier);
- 리듀서 : state를 수정하는 함수(modifier)이며, state를 인자로 받을 수 있음
- '스토어명.getState()'를 통해 state값을 가져올 수 있음
- action
- 리듀서를 통해 state값을 수정하기 위한 방법이며, 리듀서의 2번째 인자에서 사용
- state 변화를 일으키는 객체
- 선언법 :
const 리듀서명 = (state = 기본값, action) => { ... };
- 사용법 : 스토어명.dispatch({ type: 액션의종류, payload?: 관련데이터 });
- 무조건 object형태로 사용해야 하며, 'type'프로퍼티를 사용해야 함
- 2개 이상의 값을 보낼 때, 사용자정의 프로퍼티를 사용 가능
- 무조건 object형태로 사용해야 하며, 'type'프로퍼티를 사용해야 함
- ex.
const countModifier = (count = 0, { type }) => { switch (type) { case "ADD": return count + 1; case "MINUS": return count - 1; default: return count; } }; countStore.dispatch({ type: "ADD" });
- 리듀서를 통해 state값을 수정하기 위한 방법이며, 리듀서의 2번째 인자에서 사용
- 스토어명.subscribe()
- store 내부의 변화를 알려줌
- 기본형 :
스토어명.subscribe(콜백함수);
- 콜백함수 : 변화 시 실행할 함수
- ex.
countStore.subscribe(() => number.innerText = countStore.getState());
- 리듀서에서는 mutation 금지
- 원래의 값을 변형하는 행위는 금지 (.push(), .pop() 등)
- 새로운 state를 return 해야 함
- ...(spread) 등을 이용할 것
- '배열.filter()'는 새로운 배열을 만드므로, 사용 가능
- 리듀서 리팩토링
- action의 형태를 만드는 함수와 dispatch를 따로 사용하는 것을 권장
- 배열에서 원하는 항목 삭제
- id(Date.now())값과 배열.filter()를 사용
- '배열.filter()'는 원래의 배열을 변화시키지 않으면서, 새로운 변수를 생성
- ex.
const reducer = (state = [], { type, text, id}) => { switch (type) { case ADD_TODO: return [{ text, id: Date.now() }, ...state]; case DELETE_TODO: return state.filter(toDo => toDo.id !== id); default: return state; } }; const dispatchDeleteToDo = (e) => { const id = parseInt(e.target.parentNode.id); store.dispatch({ type: DELETE_TODO, id }); };
- id(Date.now())값과 배열.filter()를 사용
- React-Redux
- React에서 subscribe를 하는 state에 대해 re-rendering을 하기 위해 사용하는 라이브러리
- 설치법 :
npm i redux react-redux
- 설정법 : <Provider store={스토어명}>으로 App을 감싸줌
- connect()
- 컴포넌트를 store에 연결시켜주는 메서드 (전통적인 방식)
- '스토어명.getStore()' 대신에 사용함
- Hook API를 사용하는 것이 권장 (useSelector, useDispatch 등)
- 기본형
function mapStateToProps(state, ownProps?) { ... }; function mapDispatchToProps(dispatch, ownProps?) { ... }; connect(mapStateToProps, mapDispatchToProps?) (컴포넌트명);
- mapStateToProps 함수
- return값(object)은 컴포넌트의 prop에 추가될 것
- state : '스토어명. getStore()'의 값
- 'useSelector'로 대체 가능
- mapDispatchToProps 함수
- return값(object)은 컴포넌트의 prop에 추가될 것
- dispatch : '스토어명.dispatch()'의 값
- 'useDispatch'로 대체 가능
- ex.
interface IHomeProps { toDos: IState[]; addToDo: (text: string) => IAction; } function Home({ toDos, addToDo }: IHomeProps) { // ...... return; } function mapStateToProps(state: IState[]) { return { toDos: state }; } function mapDispatchToProps(dispatch:Dispatch<IAction>) { return { addToDo: (text: string) => dispatch(actionCreators.addToDo(text)), }; } export default connect(mapStateToProps, mapDispatchToProps)(Home);
- 컴포넌트를 store에 연결시켜주는 메서드 (전통적인 방식)
- createStore
- 23-12-16 : #4.0 ~ #4.3 / react-redux + Redux Toolkit(2)
- Redux Toolkit (RTK)
- 많은 지름길(shortcut)들이 있는 Redux 패키지
- Redux 사용 시 많은 양의 코드를 사용해야하는 불편함으로부터 탄생
- 적은 양의 Redux 코드를 짤 수 있도록 도와줌
- 설치법 :
npm i @reduxjs/toolkit
- React에서 사용 시 'redux'와 'react-redux' 패키지 또한 필요함
- 공식 문서
- 많은 지름길(shortcut)들이 있는 Redux 패키지
- createAction
- action의 type을 생성하는 메서드
- action : state 변화를 일으키는 작업을 나타내는 객체이며, action이 어떤 종류의 작업을 수행할 것인지 식별하는 문자열인 type 프로퍼티를 가지고 있음
- 선언법 :
const 액션명 = createAction<페이로드제네릭>("타입명");
- ex.
interface IAddToDo { text: string; } const addToDo = createAction<IAddToDo>("ADD");
- ex.
- reducer의 switch문에서 사용 시 '액션명.type'형태로 사용
- ex.
console.log(addToDo.type); // "ADD"
- ex.
- 실행법
const 디스패치명 = useDispatch(); 디스패치명(액션명(데이터));
- payload : action에게 보내고 싶은 데이터 (자동으로 들어감)
- 'action.payload' 형태로 사용
- payload : action에게 보내고 싶은 데이터 (자동으로 들어감)
- action의 type을 생성하는 메서드
- createReducer
- reducer를 생성하는 메서드
- reducer : 현재의 state와 action을 받아서 다음 state를 변환하는 함수
- 기본형
const 리듀서명 = createReducer(초기값 as 제네릭, (builder) => { builder .addCase(액션명, (state, action) => { // return문 }) ...... });
- state를 mutate해도 됨 (원래는 불가능했음)
- 새로운 state를 return 하거나, state를 mutate해야 함 (return 하지않음)
- RTK는 'Immer'라이브러리를 포함하고 있기 때문 (알아서 새로운 state를 만듦)
- ex.
interface IState { text: string; id: number; } const reducer = createReducer([] as IState[], (builder) => { builder .addCase(addToDo, (state, action) => { state.push({ text: action.payload, id: Date.now() }); }) .addCase(deleteToDo, (state, action) => state.filter((toDo) => toDo.id !== action.payload) ); });
- reducer를 생성하는 메서드
- configureStore
- 'createStore'메서드의 대체품이며, 미들웨어와 기본값이 추가되었음
- 기본형 :
const 스토어명 = configureStore({ reducer: 리듀서명 });
- 여러 개의 reducer들을 담은 루트리듀서 객체를 넣을 수 있음 (combineReducers()를 이용함)
- 브라우저의 'Redux DevTools' 확장프로그램을 통해 현재 state값을 확인 가능
- RTK가 아니라 Redux만 있어도 사용 가능
- createSlice
- action과 reducer를 한 번에 정의할 수 있는 함수
- 기본형
const 변수명 = createSlice({ name: 이름, initialState: 초기값 as 제네릭, reducers: { 액션명: (state, action: PayloadAction<제네릭>) => { // return문 }, ...... }, });
- 리듀서명은 '변수명.reducer'로 사용 가능
- 액션명은 '변수명.actions.액션명'으로 사용 가능
- ex.
const toDos = createSlice({ name: "toDos", initialState: [] as IState[], reducers: { add: (state, action: PayloadAction<Text>) => { state.push({ text: action.payload, id: Date.now() }); }, remove: (state, action: PayloadAction<Id>) => state.filter((toDo) => toDo.id !== action.payload), }, }); export const { add, remove } = toDos.actions; const store = configureStore({ reducer: toDos.reducer }); export default store;
- combineReducers()
- 여러 개의 reducer들을 (하나의 루트리듀서로) 합치는 데 사용하는 함수
- 기본적으로 redux store는 하나의 루트리듀서만을 사용함
- app의 state를 독립적인 reducer로 나누고, 각 reducer에 대한 action을 처리 가능
- 기본형
const 루트리듀서명 = combineReducers<제네릭>({ 리듀서명: 리듀서변수, ...... });
- ex.
// 각 리듀서를 'createSlice()'로 생성 시 const rootReducer = combineReducers({ counter: counterSlice.reducer, user: userSlice.reducer, });
- 각 reducer가 담당하는 state는 'state.리듀서명'으로 접근 가능
- 여러 개의 reducer들을 (하나의 루트리듀서로) 합치는 데 사용하는 함수
- reducer에서 currentState 복제하기
- 기본형
const 리듀서명 = (currentState, action) => { const 복제변수명 = { ...currentState }; return 복제변수명; };
- 기본형
- useSelector [READ]
- Redux store의 state를 선택하기(가져오기) 위해 사용하는 hook
- 컴포넌트 내에서 redux store의 state를 읽고, re-rendering을 함
- state가 변경될 때 마다 자동으로 re-rendering을 함
- 기본형 :
const 변수명 = useSelector(콜백함수, ?);
- 콜백함수 : store에서 필요한 state를 선택함
- 기본형 :
(state: 루트스테이트제네릭) => state.슬라이스명
- 기본형 :
- 콜백함수 : store에서 필요한 state를 선택함
- ex.
// store.ts export interface IRootState { toDos: IToDo[]; } export const toDos = createSlice({ name: "toDos", initialState: [] as IToDo[], reducers: { ... }, }); const store = configureStore({ reducer: toDos.reducer }); export default store; // 컴포넌트.tsx const toDos = useSelector((state: IRootState) => state.toDos);
- Redux store의 state를 선택하기(가져오기) 위해 사용하는 hook
- useDispatch [WRITE]
- redux store에 action을 dispatch하는 데 사용하는 hook
- 컴포넌트 내에서 redux stor에 action을 보냄 -> state 업데이트
- 선언법 :
const 디스패치명 = useDispatch();
- 기본형 :
디스패치명(액션명(?페이로드));
- ex.
const dispatch = useDispatch(); const onClick = () => dispatch(addToDo(text));
- redux store에 action을 dispatch하는 데 사용하는 hook
- Redux Toolkit (RTK)
- 23-12-19 : Redux-Persist + Deploy
- Redux-Persist 패키지
- redux의 state를 지속적으로 저장하고 복원하기 위한 라이브러리
- 웹 외에도 사용 가능 (React Native 등)
- 'recoil-persist'와 같은 기능
- 설치법 :
npm i redux-persist
- 사용법
- redux-persist의 설정객체를 생성하기
- 기본형
const 설정객체명 = { key: 키명, storage: 스토맂, blacklist?: ["리듀서명"], whitelist?: ["리듀서명"], };
- 스토리지
- localStorage :
import storage from "redux-persist/lib/storage;"
- sessionStorage :
import storageSession from "redux-persist/lib/storage/session";
- 다른 스토리지들은 공식 문서 참고
- 선언 파일을 찾을 수 없을 시 'react-app-env.d.ts'에서
/// <reference types="redux-persist" />
를 추가
- localStorage :
- ex.
import storage from "redux-persist/lib/storage"; const persistConfig = { key: "root", storage, };
- 기본형
- reducer를 Redux-Persist에 연결하는 store를 생성하기
- 기본형
const 지속리듀서명 = persistReducer(설정객체, 루트리듀서); const 스토어명 = configureStore({ reducer: 지속리듀서명 });
- 브라우저 콘솔에서 직렬화 error 시 'configureStore'에 아래와 같은 middleware 프로퍼티를 추가
middleware: (getDefaultMiddleware) => getDefaultMiddleware({ serializableCheck: false, }),
- Redux에서 값을 주고, 받을 떄 object 형태의 값을 string 형태로 변환(JSON.stringify)하는데, 이 상황에서 변환이 불가능한 값을 전달했다는 error
- 직렬화 : object -> string으로 변환하는 것 (JSON.stringify)
- 역직렬화 : string -> object로 변환하는 것 (JSON.parse)
- 참고자료
- Redux에서 값을 주고, 받을 떄 object 형태의 값을 string 형태로 변환(JSON.stringify)하는데, 이 상황에서 변환이 불가능한 값을 전달했다는 error
- ex.
const rootReducer = combineReducers({ toDos: toDos.reducer }); const persistedReducer = persistReducer(persistConfig, rootReducer); export const store = configureStore({ reducer: persistedReducer, middleware: (getDefaultMiddleware) => getDefaultMiddleWare({ serializableCheck: false, }), });
- 기본형
- 스토리지에 지속시키는 store 생성하기
- 기본형 :
const 지속스토어명 = persistStore(스토어명);
- ex.
export const persistedStore = persistStore(store);
- 기본형 :
- '<PersistGate>' 컴포넌트로 <App />을 감싸주기
- 기본형
<Provider store={store}> <PersistGate persistor={지속스토어명} loading?={로딩중보여줄컴포넌트}> <App / > </PersistGate> </Provider>
- 'persistor' 프로퍼티 : 지속스토어를 첨부
- 'loading' 프로퍼티 : [옵션] 로딩 중에 보여줄 컴포넌트
- 기본형
- redux-persist의 설정객체를 생성하기
- 공식 문서
- 참고 자료
- redux의 state를 지속적으로 저장하고 복원하기 위한 라이브러리
- Redux-Persist 패키지
- 23-12-20 : Deploy(2)
- Fix : to-do 입력 시 무조건 홈으로 이동하도록 수정
- Update : rich link preview, favicon 추가
Issue : rich link preview의 썸네일이 제대로 출력되지 않는 현상
- 23-12-22 : Deploy(3)
- Fix : rich link preview의 썸네일이 제대로 출력되지 않는 현상 수정
dition0221 / react-redux Goto Github PK
View Code? Open in Web Editor NEWRedux를 사용해 To-Do List를 생성합니다. To-Do List web site using Redux.
Home Page: https://dition0221.github.io/react-redux/