리덕스

Vorhandenheit ·2021년 11월 15일
0

React

목록 보기
13/17

리덕스 (Redux)

Redux is a predictable state container for JavaScript apps

공식 문서에서는 '리덕스는 자바스크립트 앱에 예측 가능한 상태 컨테이너'라고 말합니다.

리액트의 state를 관리하기 위한 라이브러리 입니다.

state 관리가 왜 필요할까?

리액트를 사용하다보면 부모 컴포넌트에서 자식 컴포넌트로 데이터를 보낼 때가 있습니다. 하지만 자식의 자식으로, 자식의 자식의 자식으로 데이터를 계속 그 데이터가 넘어가야 할 때면 (props drilling ), 하나의 데이터만 넘어가면 상관없지만 여러개의 데이터가 넘어갈 경우 상태관리가 복잡해집니다.
이런 관리가 복잡해지는 걸 해결하기위해 리덕스가 필요합니다.

1. 세 가지 원칙

(1) Single soure of truth

전체 상태는 단일 스토어로 관리해야합니다.

(2) State is read-only

상태는 '읽기 전용'입니다. 기존의 state 값은 수정하지않고 새로운 state를 만들어 이를 수정하는 방식으로 업데이트를 합니다.

(3) Changes are made with pure functions

리듀서는 이전의 상태와 액션 객체를 파라미터로 받아야만 하고 이전의 상태는 절대로 건드리지않고 변화를 일으킨 새로운 상태 객체를 만들어 반환해야합니다.

2. 리덕스 기본용어

액션

상태변화를 일으키기 위해서는 액션을 발생시켜야 하는데 액션을 발생시키기 위해서는 '액션 객체'라는게 필요합니다.

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"}
  }

Reducer

리듀서는 액션이 발생했을 때,현재의 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;
    }
}
  • 액션 타입별로 case문을 만들어 처리합니다.
  • 상탯값은 불변객체로 관리해야하니 수정할 때마다 새로운 객체를 생성합니다.

Store(스토어)

  • state를 관리하는 저장소의 역할을 합니다.
  • 하나의 애플리케이션에 하나의 스토어가 존재합니다.
  • 스토어는 액션이 발생하면 미들웨어 함수를 실행하고, 리듀서를 실행해 상탯값을 새로운 값으로 변경합니다.
const store = createStore(reducer) //저장소 만들기
store.subscribe(() => {
	/*...*/
}) //상태값 변경 검사

useDispatch(디스패치)

useDispatch()는 action 객체를 Reducer로 전달해주는 메소드입니다.
dispatch 함수는 dispacth(action) action인자를 넘깁니다.
이렇게 호출하면 스토어가 리듀서 함수를 실행해 함수가 넘긴 액션을 처리해 새로운 상태로 만들어줍니다.

const dispatch = useDispatch();
dispatch(change_user(user));

useSelector()

컴포넌트와 state를 연결하는 역할, useSelector()를 통해 store의 state에 접근, 저장된 값을 직접 가져옵니다.

const counter = useSelector(state => state.counter)

Subscribe(구독)

액션이 디스패치 될 때 마다 전달해준 함수를 호출합니다. 함수 형태의 값을 파타미터로 받아와, subscribe함수에 특정함수를 전달해주면 액션이 디스패치 되었을 때 마다 전달해준 함수가 호출됩니다.

3. 리덕스 사용

  • 설치
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 모듈이 같이 있는 것입니다.

  • counter 모듈
// 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;
  }
}
  • CounterContainer 컴포넌트
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;
  • Counter 컴포넌트
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로 인해 구독이 되어있기에 상태변화를 감지하고 값을 실시간으로 화면에 렌더링해 보여준다.

  • 업데이트
  1. 유저가 버튼을 클릭한다.
  2. 앱은 유저의 행동에 맞는 디스패치를 실행해 액션을 일으킨다.
  3. 스토어는 이전 상태와 현재 액션으로 리듀서 함수를 실행하고, 그 리턴 값을 새로운 상태로 저장한다.
  4. 스토어는 스토어를 구독하고 있던 UI들에게 업데이트 되었다고 알려준다.
  5. 스토어의 데이터가 필요한 각각의 UI들은 필요한 상태가 업데이트 되었는지 확인한다.
  6. 데이터가 변경된 각 구성요소는 새 데이터로 강제로 다시 렌더링하므로 화면에 표시되는 내용을 업데이트 할 수 있다.

참고

https://mjn5027.tistory.com/34

https://medium.com/lunit/%EB%8B%B9%EC%8B%A0%EC%97%90%EA%B2%8C-redux%EB%8A%94-%ED%95%84%EC%9A%94-%EC%97%86%EC%9D%84%EC%A7%80%EB%8F%84-%EB%AA%A8%EB%A6%85%EB%8B%88%EB%8B%A4-b88dcd175754

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/

profile
읽고 기록하고 고민하고 사용하고 개발하자!

0개의 댓글