Redux는 컴포넌트와 상태를 분리한다.
Redux에선 'Action → Dispatch → Reducer → store' 순서로 데이터가 단반향으로 흐른다.
- 상태가 변경되야하는 이벤트 발생 시 → 변경될 상태 정보가 담긴 'Action' 객체를 생성한다.
- Action 객체는 Dispatch함수의 인자로 전달된다.
- Dispatch함수는 -Action 객체를→ Reducer 함수로 전달한다.
- Reducer 함수는 Action 객체의 값을 확인하고 → 전역 상태 저장소인 Store의 상태를 변경한다.
- 상태가 변경되면 React는 화면을 다시 렌더링한다.
createStore
메서드를 활용해 Reducer를 연결해서 Store를 생성할 수 있다.import { createStore } from 'redux';
const store = createStore(rootReducer);
Redux를 사용하기 위해서는 redux와 react-redux를 설치해야합니다.
- DEPENDENCIES
redux, react-redux가 설치되어 있는 것을 확인하실 수 있습니다.
- index.js
안내한 순서에 따라 index.js를 완성해주세요!
1. import { Provider } from 'react-redux';
react-redux에서 Provider를 불러와야 합니다.
- Provider는 store를 손쉽게 사용할 수 있게 하는 컴포넌트입니다.
해당 컴포넌트를 불러온다음에, Store를 사용할 컴포넌트를 감싸준 후
Provider 컴포넌트의 props로 store를 설정해주면 됩니다.
2. import { legacy_createStore as createStore } from 'redux';
redux에서 createStore를 불러와야 합니다.
3. 전역 상태 저장소 store를 사용하기 위해서는 App 컴포넌트를
Provider로 감싸준 후 props로 변수 store를 전달해주여야 합니다.
주석을 해제해주세요.
4. 변수 store에 createStore 메서드를 통해 store를 만들어 줍니다.
그리고, createStore에 인자로 Reducer 함수를 전달해주어야 합니다.
(지금 단계에서는 임시의 함수 reducer를 전달해주겠습니다.)
5. 여기까지가 전역 변수 저장소를 설정하는 방법이였습니다.
브라우저 창에 오류메세지가 나타나지 않는다면 잘 적용된겁니다.👏
import React from 'react';
import { createRoot } from 'react-dom/client';
import App from './App';
// 1
import { Provider } from 'react-redux';
// 2
import { legacy_createStore as createStore } from 'redux';
const rootElement = document.getElementById('root');
const root = createRoot(rootElement);
const reducer = () => {};
// 4
const store;
root.render(
// 3
<Provider store={store}>
<App />
</Provider>
);
type
값에 따라→ 상태를 변경시키는 함수이다.const count = 1
// Reducer를 생성할 때에는 초기 상태를 인자로 요구합니다.
const counterReducer = (state = count, action) => {
// Action 객체의 type 값에 따라 분기하는 switch 조건문입니다.
switch (action.type) {
//action === 'INCREASE'일 경우
case 'INCREASE':
return state + 1
// action === 'DECREASE'일 경우
case 'DECREASE':
return state - 1
// action === 'SET_NUMBER'일 경우
case 'SET_NUMBER':
return action.payload
// 해당 되는 경우가 없을 땐 기존 상태를 그대로 리턴
default:
return state;
}
}
// Reducer가 리턴하는 값이 새로운 상태가 됩니다.
function reducer(state = 체중, action) {
if(action.type === '증가') {
state ++;
return state;
} else if(action.type === '감소') {
state --;
return state;
} else {
return state
}
}
이번에는 Reducer 함수를 완성해봅시다!
Reducer함수 첫번째 인자에는 기존 state가 들어오게 됩니다.
첫번째 인자에는 default value를 꼭 설정해주셔야 합니다!
그렇지 않을 경우 undefined가 할당되기 때문에 그로 인한 오류가 발생할 수 있습니다.
(https://redux.js.org/tutorials/fundamentals/part-3-state-actions-reducers#creating-the-root-reducer)
두번째 인자에는 action 객체가 들어오게 됩니다.
action 객체에서 정의한 type에 따라 새로운 state를 리턴합니다.
새로운 state는 전역 변수 저장소 Store에 저장되게 됩니다.
- index.js
안내한 순서에 따라 index.js를 완성해주세요!
1. 유어클래스에 있는 Reducer 예제를 복사해 임의 함수 reducer를
대체해주세요.
2. 가져온 conterReducer를 createStore에 다시 넣어주세요.
3. 주석을 해제해주세요.
4. 예제를 잘 불러오셨다면 정상적으로 화면이 나오는 것을 확인할 수 있습니다!
import React from 'react';
import { createRoot } from 'react-dom/client';
import App from './App';
import { Provider } from 'react-redux';
import { legacy_createStore as createStore } from 'redux';
const rootElement = document.getElementById('root');
const root = createRoot(rootElement);
// 1
// const reducer = () => {}
// 2
// const store;
root.render(
// 3
// <Provider store={store}>
<App />
// </Provider>
);
→ Redux의 combineReducers
메서드를 사용해 하나의 Reducer로 합쳐줄 수 있다.
import { combineReducers } from 'redux';
const rootReducer = combineReducers({
counterReducer,
anyReducer,
...
});
// payload가 필요 없는 경우
{ type: 'INCREASE' }
// payload가 필요한 경우
{ type: 'SET_NUMBER', payload: 5 }
type
payload
를 작성해 구체적인 값을 전달한다.액션 생성자(Action Creator)
// payload가 필요 없는 경우
const increase = () => {
return {
type: 'INCREASE'
}
}
// payload가 필요한 경우
const setNumber = (num) => {
return {
type: 'SET_NUMBER',
payload: num
}
이번에는 Action 객체를 완성해봅시다!
Action은 어떻게 state를 변경할지 정의해놓은 객체입니다.
Action 객체는 Dispatch 함수를 통해 Reducer 함수 두번째 인자로 전달됩니다.
Action 객체 안의 type은 필수로 지정을 해주어야 합니다.
여기서 지정한 type에 따라 Reducer 함수에서 새로운 state를 리턴하게 됩니다.
- index.js
안내한 순서에 따라 index.js를 완성해주세요!
1. 유어클래스에 있는 Action 예제 중 Action Creator 함수 increase를
복사해오세요.
2. Action Creator 함수 decrease를 만들어 주세요. type은 'DECREASE'로
설정해주세요.
3. 앞서 만든 Action Creator 함수를 다른 파일에도 사용하기 위해 export를
붙혀주세요.
import React from 'react';
import { createRoot } from 'react-dom/client';
import App from './App';
import { Provider } from 'react-redux';
import { legacy_createStore as createStore } from 'redux';
const rootElement = document.getElementById('root');
const root = createRoot(rootElement);
// 1
// 2
const count = 1;
// Reducer를 생성할 때에는 초기 상태를 인자로 요구합니다.
const counterReducer = (state = count, action) => {
// Action 객체의 type 값에 따라 분기하는 switch 조건문입니다.
switch (action.type) {
//action === 'INCREASE'일 경우
case 'INCREASE':
return state + 1;
// action === 'DECREASE'일 경우
case 'DECREASE':
return state - 1;
// action === 'SET_NUMBER'일 경우
case 'SET_NUMBER':
return action.payload;
// 해당 되는 경우가 없을 땐 기존 상태를 그대로 리턴
default:
return state;
}
// Reducer가 리턴하는 값이 새로운 상태가 됩니다.
};
const store = createStore(counterReducer);
root.render(
<Provider store={store}>
<App />
</Provider>
);
// Action 객체를 직접 작성하는 경우
dispatch( { type: 'INCREASE' } );
dispatch( { type: 'SET_NUMBER', payload: 5 } );
// 액션 생성자(Action Creator)를 사용하는 경우
dispatch( increase() );
dispatch( setNumber(5) );
⇒ Action 객체를 전달받은 Dispatch 함수는 Reducer를 호출한다.
💡 여기까지 Store, Reducer, Action, Dispatch 개념들을 코드로 구성하였다. → 각 개념들을 연결하기 위해선 **`Redux Hooks`**을 이용한다.**useSelector()
, useDispatch()
메소드**가 대표적이다.**useDispatch()**
는 Action 객체를 Reducer로 전달해 주는 Dispatch 함수를 반환하는 메서드이다.useDispatch()
를 사용해서 만든 것이다.import { useDispatch } from 'react-redux'
const dispatch = useDispatch()
dispatch( increase() )
console.log(counter) // 2
dispatch( setNumber(5) )
console.log(counter) // 5
이번에는 Dispatch 함수를 완성해봅시다!
dispatch 함수는 이벤트 핸들러 안에서 사용됩니다.
그리고 dispatch 함수는 action 객체를 Reducer 함수로 전달해줍니다.
- App.js
안내한 순서에 따라 App.js를 완성해주세요!
1. import { useDispatch } from 'react-redux';를 통해
react-redux에서 useDispatch를 불러와주세요.
2. import { increase,decrease } from './index.js';를 통해
Action Creater 함수 increase, decrease를 불러와주세요.
3. useDispatch의 실행 값를 변수에 저장해서 dispatch 함수를
사용합니다.(주석을 해제한 후 콘솔결과를 확인해보세요!)
4. 유어클래스 dispatch 예제를 참고해서 이벤트 핸들러 안에서 dispatch를
통해 action 객체를 Reducer 함수로 전달해주세요.
5. 유어클래스 dispatch 예제를 참고해서 이벤트 핸들러 안에서 dispatch를
통해 action 객체를 Reducer 함수로 전달해주세요.
import React from 'react';
import { createRoot } from 'react-dom/client';
import App from './App';
import { Provider } from 'react-redux';
import { legacy_createStore as createStore } from 'redux';
const rootElement = document.getElementById('root');
const root = createRoot(rootElement);
export const increase = () => {
return {
type: 'INCREASE',
};
};
export const decrease = () => {
return {
type: 'DECREASE',
};
};
const count = 1;
// Reducer를 생성할 때에는 초기 상태를 인자로 요구합니다.
const counterReducer = (state = count, action) => {
// Action 객체의 type 값에 따라 분기하는 switch 조건문입니다.
switch (action.type) {
//action === 'INCREASE'일 경우
case 'INCREASE':
return state + 1;
// action === 'DECREASE'일 경우
case 'DECREASE':
return state - 1;
// action === 'SET_NUMBER'일 경우
case 'SET_NUMBER':
return action.payload;
// 해당 되는 경우가 없을 땐 기존 상태를 그대로 리턴
default:
return state;
}
// Reducer가 리턴하는 값이 새로운 상태가 됩니다.
};
const store = createStore(counterReducer);
root.render(
<Provider store={store}>
<App />
</Provider>
);
import React from 'react';
import './style.css';
// 1
// 2
export default function App() {
// 3
// const dispatch = useDispatch()
// console.log(dispatch);
const plusNum = () => {
// 4
};
const minusNum = () => {
// 5
};
return (
<div className="container">
<h1>{`Count: ${1}`}</h1>
<div>
<button className="plusBtn" onClick={plusNum}>
+
</button>
<button className="minusBtn" onClick={minusNum}>
-
</button>
</div>
</div>
);
}
useSelector()
는 컴포넌트와 state를 연결하여 Redux의 state에 접근할 수 있게 해주는 메서드이다.// Redux Hooks 메서드는 'redux'가 아니라 'react-redux'에서 불러옵니다.
import { useSelector } from 'react-redux'
const counter = useSelector(state => state)
console.log(counter) // 1
이번에는 useSeletor를 완성해봅시다!
useSeletor를 통해 state가 필요한 컴포넌트에서
전역 변수 저장소 store에 저장된 state를 쉽게 불러올 수 있습니다.
- App.js
안내한 순서에 따라 App.js를 완성해주세요!
1. import { useDispatch, useSeletor } from 'react-redux';를 통해
react-redux에서 useSeletor 불러와주세요.
2. useSelector의 콜백 함수의 인자에 Store에 저장된 모든 state가
담깁니다. 그대로 return을 하게 되면 Store에 저장된 모든 state를
사용할 수 있습니다.
3. 변수 state를 콘솔에서 확인해보세요. Store에 저장된 기존 state 값인
1이 찍히는 것을 확인할 수 있습니다.
4. Store에서 꺼내온 state를 화면에 나타내기 위해 변수 state를 활용해보세요.
5. +, - 버튼을 누를 때마다 state가 변경되는 것을 확인할 수 있습니다!
import React from 'react';
import './style.css';
// 1
import { useDispatch } from 'react-redux';
import { increase, decrease } from './index.js';
export default function App() {
const dispatch = useDispatch();
// 2
// const state = useSelector((state) => state);
// 3
// console.log(state);
const plusNum = () => {
dispatch(increase());
};
const minusNum = () => {
dispatch(decrease());
};
return (
<div className="container">
{/* 4 */}
<h1>{`Count: ${1}`}</h1>
<div>
<button className="plusBtn" onClick={plusNum}>
+
</button>
<button className="minusBtn" onClick={minusNum}>
-
</button>
</div>
</div>
);
}
동일한 데이터는 항상 같은 곳에서 가지고 와야한다.
→ Redux에선 데이터를 저장하는 단 하나뿐인 공간인 Store가 있다.
상태는 읽기 전용으로, Redux의 상태는 직접 변경할 수 없다(React에서 상태갱신함수로만 상태를 변경할 수 있었던 것과 같은 이치)
→Redux에선 Action객체가 있어아만 상태를 변경할 수 있다.
상태가 엉뚱한 값으로 변경되지 않도록 변경은 순수함수로만 가능하다.
→ Reducer와 연결되는 원칙이다.