리덕스는 전역상태 관리 라이브러리이다.
-> 리덕스를 사용하면 props의 특성 중 하나인 부모 -> 자식 방향으로 전달하는 형태가 아니여도 state(상태)를 공유할 수 있어 중간에 의미없는 컴포넌트를 거치지 않아도 된다.
yarn add redux react-redux
- src > redux 폴더 생성
- redux 폴더 > config, modules 폴더
- modules > 리듀서 파일들 저장- config 폴더 > configStore.js파일
- redux : 리덕스와 관련된 코드를 모두 모아 놓을 폴더
- config : 리덕스 설정과 관련된 파일들을 놓을 폴더
- configStore : “중앙 state 관리소" 인 Store를 만드는 설정 코드들이 있는 파일
- modules : 우리가 만들 State들의 그룹, 리듀서
import { createStore } from 'redux';
import { combineReducers } from 'redux';
import 모듈스에 있는 파일 from '../modules/todos';
/*
1. createStore()
리덕스의 가장 핵심이 되는 스토어를 만드는 메소드(함수) 입니다.
리덕스는 단일 스토어로 모든 상태 트리를 관리한다고 설명해 드렸죠?
리덕스를 사용할 시 creatorStore를 호출할 일은 한 번밖에 없을 거예요.
*/
/*
2. combineReducers()
리덕스는 action —> dispatch —> reducer 순으로 동작한다고 말씀드렸죠?
이때 애플리케이션이 복잡해지게 되면 reducer 부분을 여러 개로 나눠야 하는 경우가 발생합니다.
combineReducers은 여러 개의 독립적인 reducer의 반환 값을 하나의 상태 객체로 만들어줍니다.
*/
const rootReducer = combineReducers({
모듈스에 있는 파일
});
const store = createStore(rootReducer);
export default store;
import React from 'react';
import ReactDOM from 'react-dom/client';
import './index.css';
import App from './App';
// 리덕스에 필요한 import
import store from './redux/config/configStore';
import { Provider } from 'react-redux';
const root = ReactDOM.createRoot(document.getElementById('root'));
root.render(
<Provider store={store}>
<App />
</Provider>
);
// If you want to start measuring performance in your app, pass a function
// to log results (for example: reportWebVitals(console.log))
// or send to an analytics endpoint. Learn more: https://bit.ly/CRA-vitals
modules 폴더 안의 리듀서
//todolist에 적용한 예시
// action value
const ADD_TODO = 'todos/ADD_TODO';
const DELETE_TODO = 'todos/DELETE_TODO';
const UPDATE_TODO = 'todos/UPDATE_TODO';
// action creator : action value를 return하는 함수
export const addTodo = (payload) => {
return {
type: ADD_TODO,
payload,
};
};
export const deleteTodo = (payload) => {
return {
type: DELETE_TODO,
payload,
};
};
export const updateTodo = (payload) => {
return {
type: UPDATE_TODO,
payload,
};
};
// 초기 상태값
const initialState = {
dataTodos: JSON.parse(localStorage.getItem('Main')) ?? [],
};
// 리듀서 : state에 변화를 일으키는 함수
// (1) state를 action의 type에 따라 변경하는 함수이다.
// input으로 받는 2가지 : state , action
// action 객체라는 것은 action type을 payload 만큼 처리하는 것이다.
// ex : payload가 3이다. 3만큼을 plus!
const todos = (state = initialState, action) => {
switch (action.type) {
case ADD_TODO:
// 기존 state를 복제해서 그 뒤에 새로운 객체를 추가
const newTodo = { ...state, dataTodos: action.payload };
return newTodo;
case DELETE_TODO:
// filter를 통해 id가 일치하는 내용들을 삭제!
const remainedTodo = {
...state,
dataTodos: state.dataTodos.filter((todo) => todo.id !== action.payload),
};
return remainedTodo;
case UPDATE_TODO:
return {
...state,
dataTodos: state.dataTodos.map((todo) =>
todo.id === action.payload ? { ...todo, isDone: !todo.isDone } : todo
),
};
// state.map((todo) => {
// if (todo.id === action.payload) {
// // id가 일치하는 곳에서
// return { ...todo, isDone: !todo.isDone }; //isDone의 값을 반대로(false->true or true->false) 바꿔주는 로직 구현
// // 마찬가지로 객체의 불변성을 지켜줘야 되니까 전개연산자(...)를 이용해 기존 내용을 복사해서 사용
// } else {
// // id가 일치하지 않는다면? 그냥 그대로...
// return { ...todo };
// }
// });
// return updatedTodo;
default:
return state;
}
};
// 모듈파일에서는 리듀서를 export default 한다.
export default todos;
- Action:
State가 변하는것. “무엇이 일어날지”- Reducer:
변화를 일으키는, 즉 데이터(state)를 수정하는 함수. action을 통해 어떠한 행동을 정의했다면, 그 결 과 어플리케이션의 상태가 어떻게 바뀌는지는 특정하게 되는 함수이다.- Store:
action과 action에 따라 상태를 수정하는 reducer를 저장하는 어플리케이션에 있는 단 하나의 객체. 스토어는 State 를 수시로 확인해 View 한테 변경된 사항을 알려주는 역할을 한다.- Dispatch:
스토어의 내장 함수 중 하나로 리듀서에게 Action 을 발생하라고 시키는 것 store에서 reducer함수를 실행시켜 state를 업데이트한다.- Subscribe:
액션이 디스패치 될 때 마다 전달해준 함수를 호출한다.- Middleware:
액션을 디스패치 했을때 리듀서에서 이를 처리하기에 앞서 사전에 지정된 작업들을 실행한다.
thunk 와 saga 가 대표.
- 하나의 어플리케이션은 하나의 Store만 가진다.
- 리듀서는 순수함수이다.
- 동일안 파라미터로 호출 된 리듀서는 언제나 같은 패턴의 결과값을 반환해야만 한다.
- state는 read-only 이다
기존의 state 고유 값은 수정하지 않고 새로운 state 를 만들어 이를 수정하는 방식으로 업데이트를 한다.이는 리덕스 고유의 불변성을 지키기 위함임.