[React] 상태관리 Redux
Redux
- React 생태계에서 가장 사용률이 높은 상태관리 라이브러리(다른 상태관리 Redux, MobX)
- React만 사용해도 애플리케이션을 개발할 수 있지만 규모가 커지고 구조가 조금이라도 복잡해지면 컴포넌트의 props나 State를 관리하는게 어려움 -> 그래서 상태 관리 라이브러리 redux를 사용하여 상태를 단순하게 처리함
- Store안에 State가 저장함, store에는 reducer가 반드시 있어야 함
- Reducer(Handle Action, change state = 변화를 일으키는 함수 ) 는 state와 action을 받아 적절한 로직을 처리하여 새로운 state를 반환하고 그 값을 반영하여 새롭게 render를 그리도록함
Redux Data Flow
- 엄격한 unidirectional data flow를 가짐
- 아래에 있는 Redux Flow도 좋으나 API(Application Programming Interface) 요청, 비동기 작업과 관련된 상태 관리를 위해 미들웨어를 적용하면서 Redux MiddleWar 도입필요 -> 관련 내용
Store
- 리덕스에서는 한 애플리케이션 당 하나의 스토어를 만들게 됨
- 스토어 안에는 현재 앱 상태, Reducer, 추가적으로 내장함수(ex, Dispatch, Subscribe ) 가 존재
Dispatch
- Action을 발생시키는 것(Action을 parameter로 전달 ex:dispatch(action))
- Store의 내장함수 중 하나
Subscribe
- Store의 내장함수 중 하나
- 함수 형태의 값을 파라미터로 받아옴
- Subsribe함수에 특정 함수를 전달해주면, Action이 dispatch 되었을 때마다 전달해준 함수가 호출
Redux 규칙
1. 하나의 애플리케이션 안에는 하나의 스토어가 있음
2. 상태는 읽기전용
3. 변화를 일으키는 함수, 리듀서는 순수한 함수여야 함
- 리듀서 함수는 이전 상태와, 액션 객체를 파라미터로 받음
- 이전의 상태는 절대로 건들이지 않고, 변화를 일으킨 새로운 상태 객체를 만들어서 반환
- 똑같은 파라미터로 호출된 리듀서 함수는 언제나 똑같은 결과값을 반환
=> 하지만 동일한 인풋 -> 동일한 아웃풋이 아닌 작업의 경우? 순수하지 않은 작업이기 때문에 리듀서 함수의 바깥에서 처리해줘야함 -> 리덕스 미들웨어 사용 -> rosypark.tistory.com/430
step1) 기본적인 Redux
import {createStore} from 'redux';
import { Provider} from 'react-redux';
import reducers from './reducers';
const store = createStore(reducers)
ReactDOM.render(
<Provider store={store}>
<App />
</Provider>,
document.getElementById('root')
);
step2) 개발자 도구 더하기
import { createStore } from 'redux'
import reducers from './reducers';
import { Provider } from 'react-redux';
const store = createStore(reducers, window.__REDUX_DEVTOOLS_EXTENSION__ && window.__REDUX_DEVTOOLS_EXTENSION__());
ReactDOM.render(
<Provider store={store}>
<App />
</Provider>,
document.getElementById('root')
);
step3) Redux를 잘 쓸 수 있게 해주는 middle ware 더하기
- 리덕스를 사용하면서 비동기작업(네트워크 요청)을 다룰때에는 미들웨어가 있어야지 더욱 손쉽게 상태를 관ㄱ리할 수 있음. dispatch() 메소드를 사용
여러가지 MiddleWare을 적용하는 방법
import {applyMiddleware, createStore} from 'redux';
import {Provider} from 'react-redux';
import promiseMiddleware from 'redux-promise';
import ReduxThunk from 'redux-think';
import Reducer from './_reducers/index.';
const createStoreWithMiddleware = applyMiddleware(promiseMiddleware, ReduxThunk) (createStore)
ReactDOM.render(
<Provider
store = {createStoreWithMiddleware(Reducer,
window.__REDUX_DEVTOOLS_EXTENSION__ &&
window.__REDUX_DEVTOOLS_EXTENSION__()
)} >
<App />
</Provider>,
document.getElementById('root')
);
<참고>
Props와 State
(1) Props?
- 컴포넌트 내부의 Immutable Data
- 외부(부모) 컴포넌트에게 받은 속성
- JSX 내부에 {this.props.propsName}
- 컴포넌트를 사용할 때, <> 괄호안에 propsName = "value"
- this.props.children은 기본적으로 갖고 있는 props
(2) State
- mutable한 데이터
- 독립적인 컴포넌트의 상태
- JSX 내부에 {this.state.stateName}
- this.state = {} 설정
NextRedux
Example 1 - 일반 React Redux
npx create-react-app .
npm install redux react-redux
index.js
import React from 'react';
import ReactDOM from 'react-dom';
import './index.css';
import App from './App';
import reportWebVitals from './reportWebVitals';
import {createStore} from 'redux';
//ACTION Increment
const increment = () => {
return {
type : 'INCREMENT'
}
}
const decrement = () => {
return {
type : 'DECREMENT'
}
}
//Reducer
const counter = (state = 0, action ) => {
switch(action.type){
case 'INCREMENT' :
return state + 1 ;
case 'DECREMENT':
return state - 1;
}
}
//STORE -> Globalized State
let store = createStore(counter);
//Display it in the console
store.subscribe(()=> console.log(store.getState()));
//Dispatch = Action
store.dispatch(increment());
store.dispatch(increment());
store.dispatch(decrement());
ReactDOM.render(
<React.StrictMode>
<App />
</React.StrictMode>,
document.getElementById('root')
);
// 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
reportWebVitals();
Example 2 - 일반 React Redux
Example
App.js
index.js
actions
- index.js
reducers
- counter.js
- index.js
- isLogged.js
App.js
import './App.css';
import {useSelector, useDispatch} from 'react-redux';
import {increment, decrement} from './actions';
function App() {
const counter = useSelector(state => state.counter);
const isLogged = useSelector(state => state.isLogged);
const dispatch = useDispatch();
return (
<div className="App">
<h1> Counter {counter} </h1>
<button onClick = {()=> dispatch(increment())}> + </button>
<button onClick = {()=> dispatch(decrement())}> - </button>
{isLogged? <h3> Valuable Information I shouldn't see</h3> : ''}
</div>
);
}
export default App;
index.js
import React from 'react';
import ReactDOM from 'react-dom';
import './index.css';
import App from './App';
import reportWebVitals from './reportWebVitals';
import {createStore} from 'redux';
import allReducer from './reducers';
import {Provider} from 'react-redux';
const store = createStore(
allReducer,
window.__REDUX_DEVTOOLS_EXTENSION__ && window.__REDUX_DEVTOOLS_EXTENSION__()
);
ReactDOM.render(
<Provider store = {store}>
<App />
</Provider>,
document.getElementById('root')
);
// 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
reportWebVitals();
- React에서 Redux는 페이지는 Provider로 감싸준다.
reducers - counter.js
const counterReducer = (state = 0, action ) => {
switch(action.type){
case 'INCREMENT' :
return state + action.payload;
case 'DECREMENT' :
return state - 1 ;
default:
return state;
}
}
export default counterReducer;
reducers - index.js
import counterReducer from './counter';
import loggedReducer from './isLogged';
import {combineReducers} from 'redux';
const allReducers = combineReducers({
counter : counterReducer,
isLogged : loggedReducer
});
export default allReducers;
reducers - isLogged.js
const loggedReducer = (state= false, action) => {
switch(action.type){
case 'SIGN_IN':
return !state;
default:
return state;
}
};
export default loggedReducer;
actions - index.js
export const increment = (number) => {
return {
type : 'INCREMENT',
payload : number
};
};
export const decrement = () => {
return {
type : 'DECREMENT'
};
};
Redux dev Tool Extension
1. 바로 설치
npm install redux-devtools-extension
2. Googld DevTools 확장
chrome.google.com/webstore/detail/redux-devtools/lmhkpmbekcpmknklioeibfkpmmfibljd
<출처>
1. react.vlpt.us/using-typescript/
2.d2.naver.com/helloworld/1848131
5.