Redux is a predictable state container for JavaScript apps
공식 문서에서는 '리덕스는 자바스크립트 앱에 예측 가능한 상태 컨테이너'라고 말합니다.
리액트의 state를 관리하기 위한 라이브러리 입니다.
리액트를 사용하다보면 부모 컴포넌트에서 자식 컴포넌트로 데이터를 보낼 때가 있습니다. 하지만 자식의 자식으로, 자식의 자식의 자식으로 데이터를 계속 그 데이터가 넘어가야 할 때면 (props drilling ), 하나의 데이터만 넘어가면 상관없지만 여러개의 데이터가 넘어갈 경우 상태관리가 복잡해집니다.
이런 관리가 복잡해지는 걸 해결하기위해 리덕스가 필요합니다.
전체 상태는 단일 스토어로 관리해야합니다.
상태는 '읽기 전용'입니다. 기존의 state 값은 수정하지않고 새로운 state를 만들어 이를 수정하는 방식으로 업데이트를 합니다.
리듀서는 이전의 상태와 액션 객체를 파라미터로 받아야만 하고 이전의 상태는 절대로 건드리지않고 변화를 일으킨 새로운 상태 객체를 만들어 반환해야합니다.
상태변화를 일으키기 위해서는 액션을 발생시켜야 하는데 액션을 발생시키기 위해서는 '액션 객체'라는게 필요합니다.
const Add_Todo = 'Add_Todo';
{ type : Add_Todo,
text : "reduex app start"
}
액션 객체는 이와 같은 형태를 가지고 있습니다. 객체의 형태를 하고 , type을 필수로 가져야합니다.
액션객체와 함께 dispatch 메서드를 호출하면 상탯값이 변경됩니다.
store.dispatch({type : 'ADD', title: "adf", priority: "high"})
store.dispatch({type : 'Remove', id: "12"})
store.dispatch({type : 'REMOVE_ALL'}) //store에 dispatch를 전달
액션 생성함수를 이용하여 파라미터를 받아와 액션을 미리 정의해줄 수 있습니다.
function addTodo({title, priority}) {
return {
type : Add_Todo,
title :
}
}
function removeAllTodo() {
return {type : "todo/REMOVE_ALL"}
}
리듀서는 액션이 발생했을 때,현재의 state와 Action을 인자로 받아서 Store에 접근해 Action에 맞춰서 state를 변경시킵니다.
function reducer(state, action) {
switch(action.type) {
case 'Number_count' :
return state + 1;
case 'Change_input' :
return state;
default :
return state;
}
}
const store = createStore(reducer) //저장소 만들기
store.subscribe(() => {
/*...*/
}) //상태값 변경 검사
useDispatch()는 action 객체를 Reducer로 전달해주는 메소드입니다.
dispatch 함수는 dispacth(action) action인자를 넘깁니다.
이렇게 호출하면 스토어가 리듀서 함수를 실행해 함수가 넘긴 액션을 처리해 새로운 상태로 만들어줍니다.
const dispatch = useDispatch();
dispatch(change_user(user));
컴포넌트와 state를 연결하는 역할, useSelector()를 통해 store의 state에 접근, 저장된 값을 직접 가져옵니다.
const counter = useSelector(state => state.counter)
액션이 디스패치 될 때 마다 전달해준 함수를 호출합니다. 함수 형태의 값을 파타미터로 받아와, subscribe함수에 특정함수를 전달해주면 액션이 디스패치 되었을 때 마다 전달해준 함수가 호출됩니다.
npm install redux
import React from 'react';
import ReactDOM from 'react-dom';
import App from './App';
import * as serviceWorker from './serviceWorker';
import { createStore } from 'redux';
import { Provider } from 'react-redux';
import rootReducer from './modules';
import { composeWithDevTools } from 'redux-devtools-extension';
// rootReducer 를 가진 Store 생성
const store = createStore(rootReducer, composeWithDevTools());
ReactDOM.render(
<Provider store={store}>
<App />
</Provider>,
document.getElementById('root')
);
serviceWorker.unregister();
제일 먼저 index.js에서 createStore 함수를 이용해서 store를 만들어야합니다. 이때 rootReducer 파일로 가면
Provide는 react-rudx 기능 중 하나로 하위 컴포넌트들에게 공급해주는 역할을 합니다. 로드해온 스토어의 상태등을 하위 컴포넌트에 전달하여 렌더링합니다.
import { combineReducers } from 'redux';
import counter from './counter';
import todos from './todos';
const rootReducer = combineReducers({
counter,
todos
});
export default rootReducer;
combineReducers 라는 함수로 counter 모듈과 todos 모듈을 하나의 모듈로 합쳐셔, rootReducer라는 이름으로 export시켜주고 있습니다.
store에는 counter 모듈과 todos 모듈이 같이 있는 것입니다.
// Action type ( 액션 타입 )
const SET_DIFF = 'counter/SET_DIFF';
const INCREASE = 'counter/INCREASE';
const DECREASE = 'counter/DECREASE';
// Action Creator Function ( 액션 생성 함수 )
export const setDiff = diff => ({ type: SET_DIFF, diff });
export const increase = () => ({ type: INCREASE });
export const decrease = () => ({ type: DECREASE });
// init State ( 초기 상태 )
const initialState = {
number: 0,
diff: 1
};
// Reducer function ( 리듀서 함수 )
export default function counter(state = initialState, action) {
switch (action.type) {
case SET_DIFF:
return {
...state,
diff: action.diff
};
case INCREASE:
return {
...state,
number: state.number + state.diff
};
case DECREASE:
return {
...state,
number: state.number - state.diff
};
default:
return state;
}
}
import React from 'react';
import { useSelector, useDispatch } from 'react-redux';
// useSelector 리덕스 스토어의 상태를 조회하는 기능을 수행합니다.
import Counter from '../components/Counter';
import { increase, decrease, setDiff } from '../modules/counter';
function CounterContainer() {
const { number, diff } = useSelector(state => ({
number: state.counter.number,
diff: state.counter.diff
}));
const dispatch = useDispatch(); //dispatch를 사용합니다
const onIncrease = () => dispatch(increase()); // 각 액션을 dispatch하는 함수를 생성
const onDecrease = () => dispatch(decrease());
const onSetDiff = diff => dispatch(setDiff(diff));
return (
<Counter
number={number}
diff={diff}
onIncrease={onIncrease}
onDecrease={onDecrease}
onSetDiff={onSetDiff}
/>
);
}
export default CounterContainer;
import React from 'react';
function Counter({ number, diff, onIncrease, onDecrease, onSetDiff }) {
const onChange = e => {
onSetDiff(parseInt(e.target.value, 10));
};
return (
<div>
<h1>{number}</h1>
<div>
<input type="number" value={diff} min="1" onChange={onChange} />
<button onClick={onIncrease}>+</button>
<button onClick={onDecrease}>-</button>
</div>
</div>
);
}
export default Counter;
onClick 이벤트로 CounterContainer.js 넘어온 disatch가 실행된다
숫자 증가 버튼을 클릭하면, 스토어에 내장되어있는 reducer 중 counter.js 모듈의 리듀서를 수행해 값이 증가하고, 스터오의 상태에 view가 이전의 useSelector로 인해 구독이 되어있기에 상태변화를 감지하고 값을 실시간으로 화면에 렌더링해 보여준다.
https://mjn5027.tistory.com/34
https://kyun2da.dev/%EB%9D%BC%EC%9D%B4%EB%B8%8C%EB%9F%AC%EB%A6%AC/Redux-%EC%A0%95%EB%A6%AC/