Redux 기본 개념
들어가며
오늘 프로젝트 일지는 Redux에 대해 정리했다. 이전에 과정에 있었지만 배운지 시간도 오래 지났고 한동안 사용하지 않았기 때문에 다 까먹었다. 😋 팀원들과 모여서 Redux를 공부했는데 심화된 내용으로 갈수록 어려워지더라. 하루를 투자할만한 가치가 충분했다. 하루가 지나면 기억이 애매해지기 때문에 글로 남긴다.
내가 진행하면서 참고한 페이지는 벨로퍼트님의 페이지다.
벨로퍼트와 함께하는 모던 리액트
Redux 기본 개념
Action
- 상태에 변화가 필요할 때 액션을 발생시킨다. 단어만 봐서는 동적인 의미가 있어 보이지만 액션은 정적인 객체다.
{
type: "TOGGLE_VALUE"
}
Action 생성 함수
- 정적인 객체를 쉽게 조작할 수 있게 만드는게 액션 생성 함수다. 인자값을 가지고 액션 객체를 반환한다.
- 다른 파일에서 불러와서 사용하기 때문에
export
키워드를 붙인다.
export function addTodo(data) {
return {
type: "ADD_TODO",
data
};
}
// 화살표 함수로도 만들 수 있습니다.
export const changeInput = text => ({
type: "CHANGE_INPUT",
text
});
Reducer
- 변화를 일으키는 함수다. 기존
state
와 새로운 action
을 받아온다.
- 리덕스는 순수 함수이기 때문에 기존 값을 변경하는 조작은 허용하지 않는다.
객체 형태의 상태일 경우, ...
스프레드 문법을 사용해서 새로운 객체를 생성하고 그 객체에 변경 값을 저장해야 된다.
- 여러개의 리듀서를 사용한다면 서브 리듀서들을 합쳐서 루트 리듀서를 만들 수 있다.
function counter(state, action) {
switch (action.type) {
case 'INCREASE':
return state + 1;
case 'DECREASE':
return state - 1;
default:
return state;
}
}
Store
- 리덕스는 한 애플리케이션당 하나의 스토어를 가지고 있다. 현재 상태와 리듀서가 있고 추가로 몇가지 내장 함수가 있다.
Dispatch (보내다, 파견하다, 발송하다)
- 스토어의 내장 함수다. 액션을 매개변수로 받고 리듀서로 보낸다.
function CounterContainer() {
const number = useSelector((state) => state.counter);
const dispatch = useDispatch();
const onIncrease = () => {
dispatch(increase());
};
Subscribe
- 스토어의 내장 함수다. subscribe 함수에 특정 함수를 전달해주면 액션이 디스패치 되었을 때 마다 전달해준 함수가 호출된다.
- 자주 사용하지는 않고
connect
또는 useSelector
Hook을 사용하여 리덕스 스토어 상태에 구독한다.
Redux 3가지 규칙
- 하나의 애플리케이션 안에는 하나의 스토어가 있습니다.
- 상태는 읽기 전용입니다.
- 변화를 일으키는 함수, 리듀서는 순수한 함수여야 합니다.
Redux 디렉토리 구조
{
type: "TOGGLE_VALUE"
}
export
키워드를 붙인다.export function addTodo(data) {
return {
type: "ADD_TODO",
data
};
}
// 화살표 함수로도 만들 수 있습니다.
export const changeInput = text => ({
type: "CHANGE_INPUT",
text
});
state
와 새로운 action
을 받아온다. 객체 형태의 상태일 경우,
...
스프레드 문법을 사용해서 새로운 객체를 생성하고 그 객체에 변경 값을 저장해야 된다. function counter(state, action) {
switch (action.type) {
case 'INCREASE':
return state + 1;
case 'DECREASE':
return state - 1;
default:
return state;
}
}
function CounterContainer() {
const number = useSelector((state) => state.counter);
const dispatch = useDispatch();
const onIncrease = () => {
dispatch(increase());
};
connect
또는 useSelector
Hook을 사용하여 리덕스 스토어 상태에 구독한다.
- 하나의 애플리케이션 안에는 하나의 스토어가 있습니다.
- 상태는 읽기 전용입니다.
- 변화를 일으키는 함수, 리듀서는 순수한 함수여야 합니다.
Redux 디렉토리 구조
액션과 리듀서가 서로 다른 파일에 저장하는게 공식 권장 사항이었다.
하지만 그러면 조작이 상당히 불편해서 리듀서와 액션 관련 코드를 하나의 파일에 몰아서 저장한다.
이를 Ducks 패턴이라고 부른다.
- index.js
스토어가 만들어지는 파일 - src/pages
페이지가 저장되는 디렉토리 - src/components
컴포넌트가 저장되는 디렉토리 - src/containers
- src/modules
index.js 파일에서 루트 리듀서를 만든다.
액션과 리듀서가 저장된 파일이 모여있는 디렉토리- 액션 타입 선언
- 액션 생성함수 선언
- 초기 상태 선언
- 리듀서
Redux 모듈 만들기
/* 액션 타입 만들기 */
// Ducks 패턴을 따를땐 액션의 이름에 접두사를 넣어주세요.
// 이렇게 하면 다른 모듈과 액션 이름이 중복되는 것을 방지 할 수 있습니다.
const SET_DIFF = 'counter/SET_DIFF';
const INCREASE = 'counter/INCREASE';
const DECREASE = 'counter/DECREASE';
/* 액션 생성함수 만들기 */
// 액션 생성함수를 만들고 export 키워드를 사용해서 내보내주세요.
export const setDiff = diff => ({ type: SET_DIFF, diff });
export const increase = () => ({ type: INCREASE });
export const decrease = () => ({ type: DECREASE });
/* 초기 상태 선언 */
const initialState = {
number: 0,
diff: 1
};
/* 리듀서 선언 */
// 리듀서는 export default 로 내보내주세요.
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;
}
}
useSelector 최적화
/* 액션 타입 만들기 */
// Ducks 패턴을 따를땐 액션의 이름에 접두사를 넣어주세요.
// 이렇게 하면 다른 모듈과 액션 이름이 중복되는 것을 방지 할 수 있습니다.
const SET_DIFF = 'counter/SET_DIFF';
const INCREASE = 'counter/INCREASE';
const DECREASE = 'counter/DECREASE';
/* 액션 생성함수 만들기 */
// 액션 생성함수를 만들고 export 키워드를 사용해서 내보내주세요.
export const setDiff = diff => ({ type: SET_DIFF, diff });
export const increase = () => ({ type: INCREASE });
export const decrease = () => ({ type: DECREASE });
/* 초기 상태 선언 */
const initialState = {
number: 0,
diff: 1
};
/* 리듀서 선언 */
// 리듀서는 export default 로 내보내주세요.
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;
}
}
특정 상태를 조작했을 때 모든 상태가 다시 랜더링 되는 것을 막으면 최적화를 달성할 수 있다.
useSelector
를 여러번 사용하는 방법
const number = useSelector(state => state.counter.number);
const diff = useSelector(state => state.counter.diff);
- react-redux의 shallowEqual 함수를 useSelector의 두번째 인자로 전달해주는 방법
import React from 'react';
import { useSelector, useDispatch, shallowEqual } from 'react-redux';
import Counter from '../components/Counter';
import { increase, decrease, setDiff } from '../modules/counter';
function CounterContainer() {
// useSelector는 리덕스 스토어의 상태를 조회하는 Hook입니다.
// state의 값은 store.getState() 함수를 호출했을 때 나타나는 결과물과 동일합니다.
const { number, diff } = useSelector(
state => ({
number: state.counter.number,
diff: state.counter.diff
}),
shallowEqual
);
(...)
Author And Source
이 문제에 관하여(Redux 기본 개념), 우리는 이곳에서 더 많은 자료를 발견하고 링크를 클릭하여 보았다 https://velog.io/@shleecloud/Redux-기본-개념저자 귀속: 원작자 정보가 원작자 URL에 포함되어 있으며 저작권은 원작자 소유입니다.
우수한 개발자 콘텐츠 발견에 전념 (Collection and Share based on the CC Protocol.)