11-1.Redux-basic
Updated:
Latest update : 2021-10-1 (Redux basic) - 4.Action 부터
-
Redux 를 쓰는 이유..
1.복잡한 props 전송이 필요 없음 2.모든 컴포넌트가 직접 데이터를 꺼내서 쓸 수 있음 3.state 데이터 관리 기능
1.설치 및 사용
$ yarn add redux react-redux
redux 는 데이터를 엄격하게 관리하는 기능, react-redux 는 리덕스를 리엑트에서 쓸 수 있게 도와주는 기능을 제공
1-1.index.js 에서 redux 초기 설정
redux를 이용한 개발환경을 셋팅하시려면 index.js를 열어 다음과 같이 작성합니다..
// in index.js
import { Provider } from "react-redux";
ReactDOM.render(
<React.StrictMode>
<BrowserRouter>
<Provider>
<App />
</Provider>
</BrowserRouter>
</React.StrictMode>
);
-
라는걸 import 해오신 다음에 -
내가 state값 공유를 원하는 컴포넌트를 다 감싸시면 됩니다.
-
컴포넌트를 감쌌습니다.
그럼
🔷 이것이 redux를 이용하는 첫번째 이유입니다
컴포넌트가 매우 깊숙히 있다면 state전달하려고 props 100번 써야되고 귀찮은데
redux를 이렇게 셋팅해주시면 props 100번 쓰실 필요가 없습니다. 바로 꺼내쓰실 수 있습니다.
(Context 그 문법이랑 매우 비슷합니다.)
📌 redux에서 state를 하나 만드시려면 createStore() 함수를 쓰셔야합니다.
import { Provider } from "react-redux";
import { createStore } from "redux";
let store = createStore(() => {
return [{ id: 0, name: "niceShoes", quan: 2 }];
});
ReactDOM.render(
<React.StrictMode>
<BrowserRouter>
<Provider>
<App />
</Provider>
</BrowserRouter>
</React.StrictMode>
);
import 해오신 다음에 createStore(콜백함수) 이렇게 사용하시면 되며
콜백함수엔 뭘 작성하냐면.. 내가 원하는 state 초기값으로 내보낼 값을 정해서 .
그럼 store 에 저장된 state 를 사용할 수 있습니다.
이제 <Provider>
에 만든 state 를 props 처럼 등록해서 사용하면 됩니다.
import { Provider } from "react-redux";
import { createStore } from "redux";
let store = createStore(() => {
return [{ id: 0, name: "niceShoes", quan: 2 }];
});
ReactDOM.render(
<React.StrictMode>
<BrowserRouter>
<Provider store={store}>
<App />
</Provider>
</BrowserRouter>
</React.StrictMode>
);
redux 설치 후엔 state들을 store라는 명칭으로 부릅니다. 아까 변수명이 store이기도 하고요
1-2.store에 있는 state 데이터 꺼내쓰는 법
-
예시로 Cart.js 라는 파일을 만들어 table 생성 후, 그 테이블 안에 store 에 저장된 데이터를 데이터 바인딩해서 사용하기
-
그냥 바로 사용하면 안되고 store 안에 있는 데이터를 props 의 형태로 등록해서 사용해야 합니다.
// connect 를 import 해옴 redux 로 부터
import { connect } from "react-redux";
function Cart(props) {
return (
<div>
// react bootstrap 으로 table 하나 생성함
<Table>
<tr>
<th>#</th>
<th>상품명</th>
<th>수량</th>
<th>변경</th>
</tr>
<tr>
<td>1</td>
// 아래와 같이 state 를 props 화 시켜서 원하는 data 를 바인딩해서 쓸 수
있음
<td>{props.state[0].name}</td>
<td>Table cell</td>
<td>Table cell</td>
</tr>
</Table>
</div>
);
}
// state 를 props 화 해서 쓰는것
let storeData = (state) => {
return {
state,
};
};
// 이거는 connect 를 사용해서 store 에 연결 시키고 이거는 패턴이니 잘 알아둬야 함
export default connect(storeData)(Cart);
1-3.setting 요약
셋팅은
1.index.js에
2.state 값공유를 원하는 컴포넌트를 감싸면 됩니다.
3.createStore를 import 해오신 다음 사용법에 의해 state를 만들어 let store라는 변수에 저장합니다.
4.<Provider store={store}> 이렇게 store를 등록하면
이제 Provider로 감싼 컴포넌트는 전부 store안에 있던 값을 props없이 공유 가능합니다.
store안에 있던 state 사용은
원하는 컴포넌트 파일 가셔서
1.하단에 function state를props화() 를 하나 만들어주고 state를 props로 등록합니다.
2.그리고 또 하단에 export default connect(state를props화)(Cart);
이렇게 사용하시면 이제 아까 만들어둔 state가 props로 등록이 된 것입니다.
props.state이름 이렇게 저장된 state를 자유롭게 사용할 수 있습니다.
이와 같이 한번 셋팅하고 오나료된 모든 component 는 redux 내에서 state 를 자유지재로 사용 가능합니다.
2.reducer,dispatch 로 데이터 수정하는 법
- redux 에선 state 데이터의 수정방법을 미리 정의합니다. : reducer
아래 예제는 수량을 + 버튼 누르면 수량 증가, - 버튼을 누르면 수량 감소하는 reducer 만들기
// index.js
// state 원본 자료
const defaultState = [
{ id: 0, name: "niceshoe", quant: 2 },
{ id: 1, name: "niceshoe2", quant: 1 },
];
// state 수정을 위한 reducer 생성
const reducer = (state = defaultState, action) => {
if (action.type === "quanIncrease") {
const copy = [...state]; // deep copy 한 state 만들어서
copy[0].quant++; // 첫번째 id 에서 수량이 바뀌게 만들기
return copy; // 변경된 data 인 copy state return
} else if (action.type === "quanDecrease") {
const copy = [...state];
copy[0].quant--;
return copy;
} else {
// 변경된것이 없을경우 그냥 state return
return state;
}
};
const store = createStore(reducer); // store 생성
데이터 수정하기
// in cart.js
// 버튼 클릭시, 수량이 증가되게 만들기 (dispatch 사용해서 store data 가져오기)
<button onClick={() => { props.dispatch({ type: 'quanIncrease' }) }}>+</button>
<button onClick={() => {props.dispatch({type: 'quanDecrease'})}}>-</button>
let storeData = state => {
return {
state
}
}
export default connect(storeData)(Cart);
3.state 와 reducer 가 더 필요할 경우
-
reducer 를 하나 더 만들경우 combineReducers 를 통해서 파일을 reducer 들을 묶어 줘야 함
-
예제 alert 창 만들어서 닫기 동작 버튼 만들기
// index.js
const defaultAlert = true;
const reducer2 = (state = defaultAlert, action) => {
if (action.type === "alertClose") {
state = false;
return state;
} else {
return state;
}
};
// 기존 reducer 와 함꼐 사용하기 위해서 combineReducers 사용함
const store = createStore(combineReducers({ reducer, reducer2 }));
2021-10-1
추가 업데이트 Redux Basic
4.Action
-
Redux 에서 action 은 사실 그냥 객체 (Object) 입니다.
-
store 에 전달이 되면 스토어의 상태를 변경하느데 사용됩니다.
-
두가지 형태의 액션이 있습니다.
-
{type: ‘TEST’} // payload 없는 액션
-
{type: ‘TEST’, params: ‘hello’} // payload 있는 액션
-
-
type 만이 필수 프로퍼티 이며, type 은 문자열 입니다.
redux 액션 생성자
function 액션생성자 (…arg) {return 액션} 이런식의 패턴으로 action 을 생성합니다.
-
액션을 생성하는 함수를 ‘액션 생성자 (Action Creator)’ 라고 합니다.
-
함수를 통해 액션을 생성해서, 액션 객체를 리턴해줍니다.
-
createTest(‘hello’); // {type: ‘TEST’, params: ‘hello’} 리턴
리덕스에서 action은 어떠한 일은 하는가?
-
action 생성자를 통해 action을 만들어 냅니다.
-
만들어낸 action 객체를 리덕스 스토어에 보냅니다
-
리덕스 트오어가 action 객체를 받으면 스토어의 상태 값이 변경 됩니다.
-
변경된 상태 값에 의해 상태를 이용하고 있는 컴포넌트가 변경됩니다.
-
action은 스토에 보내는 일종의 input 이라고 생각하면 됩니다.
action을 준비하기 위해서는?
-
action의 타입을 정의하여 변수로 빼는 단계
-
강제는 아닙니다. (그러므로 안해도 됩니다)
-
그냥 타입을 문자열로 넣기에는 실수를 유발할 가능성이 큽니다.
-
미리 정의한 변수를 사용하면, 스펠링에 주의를 덜 기울여도 됩니다.
-
-
action 객체를 만들어 내는 함수를 만드는 단계
-
하나의 action 객체를 만들기 위해 하나의 함수를 만들어냅니다.
-
action의 타입은 미리 정의한 타입 변수로 부터 가져와서 사용합니다.
-
보통 actions 의 type 은 대문자에 underbar _ 를 사용해소 표현합니다.
// 예시 add_todo 라는 객체를 만들어서 todo 에 집어 넣는 예시
export const ADD_TODO = "ADD_TODO";
const addTodo = (todo) => {
return {
type: ADD_TODO,
todo,
};
};
5.Reducer
-
action 을 주면, 그 action 이 적용되어 달라진(안달라질수도 있긴한데.) 결과를 만들어 줍니다.
-
그냥 함수라고 보면 됩니다.
-
Pure function : 같은 input 을 받으면 같은 결과를 내는 것
-
Immutable : reducer 를 통해 스테이트가 달라졌을을 redux 가 인지하는 방식 - 원래의 객체와 새로 만들어진 객체가 별도로 만들어져야 된다는 것
-
reducer 형태
function 리듀서(previousState, action) {
return newState;
}
-
action 을 받아서 state 를 return 하는 구조
-
인자로 들어오는 previousState 와 리턴되는 newState 는 다른 참조를 가지도록 해야 합니다.
// reducer 작성 예시
import { ADD_TODO } from "./actions";
// state
// ['test', 'test2']; 이런식의 형태로 state 가 되어 있음
const initialState = [];
// previousState 가 undefined 아무것도 안들어 올경우 initialState 로 초기화를 자동으로 해줌
export const todoApp = (previousState = initialState, action) => {
// 기존 action 의 type 이 ADD_TODO 와 같을 경우 이전것과 action.todo 를 합쳐서 return 해줌
if (action.type === ADD_TODO) {
return [...previousState, action.todo];
}
return previousState; // 어떠한 변화도 있지 않다면 계속 action 을 받더라도 아무 변화가 없음
};
6.createStore
- 스토어를 만드는 함수
const store = createStore(reducer);
createStore<S>(
reducer:Reducer<S>, // 첫번째 인자는 reducer 함수
preloadedState:S, // initial state 를 넣을수 있는데 undefined 경우에는 reducer 에서 initial 값을 설정해서 사용함
enhancer?: StoreEnhancer<S>
): Store<S>;
store 구조
-
store.getState();
- 현재 스토어의 state 를 가져오는 함수 -
store.dispatch(action); 또는 store.dispatch(action());
- 액션을 인자로 넣어서 store 의 상태를 변경시키는 것 -
const unsubscribe = store.subscribe(()=> {});
- store 에 변경이 생겼을 경우 subscribe 안에 함수를 실행하는 명령어 그 결과물이 unsubscribe 역활을 하는것인데-
return 이 unsubscribe 라는 점 !
-
unsubscribe() 하면 subscribe 되는 함수를 제거
-
-
store.replaceReducer(다른 리듀서)
- 원래 가지고 있는 reducer 를 다른 reducer 로 바꾸는 역활을 하는것
7.CombineReducers
-
redux 로 부터 import 해서 사용하는 함수 로서 redux 는 단일 스토어기 때문에 app 에 데이터가 많아지고 복잡해지면 reducer 안이 목잡하게 됨 : store 를 여러개 사용할 수 없습니다.
-
그래서 reducer 안에 내용을 쪼개는 능력이 필요합니다. 같은 속성값의 reducer 끼리 쪼개고 그다음에 다시 함쳐서 reducer 를 사용하는 것이 combineReducer 입니다.
// combine reducer 에서 대표해서 각 reducer 를 만들어 주고 각 reducer 에서 쪼개서 사용함
import { combineReducers } from "redux";
import todos from "./todos";
import filter from "./filter";
const reducer = combineReducers({
todos,
filter,
});
export default reducer;
// in todos reducer
import { ADD_TODO, COMPLETE_TODO } from "../actions";
const initialState = [];
const todos = (previousState = initialState, action) => {
// 기존 action 의 type 이 ADD_TODO 와 같을 경우 이전것과 action.todo 를 합쳐서 return 해줌
if (action.type === ADD_TODO) {
return [...previousState, { text: action.text, done: false }];
}
if (action.type === COMPLETE_TODO) {
return previousState.map((todo, index) => {
if (index === action.index) {
return { ...todo, done: true };
}
return todo;
});
}
return previousState; // 어떠한 변화도 있지 않다면 계속 action 을 받더라도 아무 변화가 없음
};
export default todos;
// store 생성 시 combineReducer 만 가져와서 사용하게 되면 됨
// in store.js
import { createStore } from "redux";
import todoApp from "./reducers/reducer";
const store = createStore(todoApp);
export default store;
8.Redux를 React에 연결하기
react-redux
-
Provider component를 제공해 줍니다.
-
connect 함수를 통해 “container”를 만들어 줍니다
- container는 store의 state와 dispatch(액션)를 연결한 component에 props로 넣어주는 역활을 합니다.
-
필요한 사항은?
-
어떤 state를 어떤 props 에 연결할 것인지에 대한 정의
-
어떤 dispatch(액션)을 어떤 props 에 연결할 것인지에 대한 정의
-
그 props를 보낼 컴포넌트를 정의
-
예시) containers 를 만들어서 component 에 redux state 를 뿌려주는 것
// TodoListContainer.jsx
import { useSelector } from "react-redux";
import TodoList from "../components/TodoList";
const TodoListContainer = () => {
const todos = useSelector((state) => state.todos);
return <TodoList todos={todos} />;
};
export default TodoListContainer;
const TodoList = ({ todos }) => {
return (
<ul>
{todos.map((todo) => {
return <li>{todo.text}</li>;
})}
</ul>
);
};
export default TodoList;
🔶 🔷 📌 🔑
Reference
-
React redux official site - https://react-redux.js.org/tutorials/quick-start
-
coding apple - https://online.codingapple.com/course/react-basic/
Leave a comment