Redux

Haeun·2023년 1월 7일
0

Redux(리덕스)란?

  • JavaScript 상태 관리 라이브러리이다.
  • Redux의 본질은 Node.js 모듈이다.

Redux의 기본 개념

1. Single source of truth

동일한 데이터는 항상 같은 곳에서 가지고 온다.

즉, 스토어라는 하나 뿐인 데이터 공간이 있다는 의미이다.

2. State is read-only

리액트에서는 setState 메소드를 활용해야만 상태 변경이 가능하다.

리덕스에서도 액션이라는 객체를 통해서만 상태를 변경할 수 있다.

3. Changes are made with pure functions

변경은 순수 함수로만 가능하다. 리듀서와 연관되는 개념이다.

Store(스토어) – Action(액션) – Reducer(리듀서)



🕵🏻‍♀️ 리덕스를 사용하는 이유

  1. 단방향 데이터 흐름의 탄생

    • 양방향 데이터 흐름은 한 개의 모델이 여러 뷰를 조작하거나, 한 개의 뷰가 여러 모델을 조작할 수 있다. 이는 데이터 흐름을 이해하고 버그를 찾는 것을 방해한다. 이와 같은 이유로 단방향 데이터 흐름이 탄생했다.
    • 단방향 데이터 흐름(=redux가 작동하는 방식)은 데이터가 한 방향으로만 흐르기 때문에 데이터 흐름을 예측하기 쉽다.
  2. 편리한 state 관리

    • redux를 사용하지 않으면 react 컴포넌트 들은 각각 개별적으로 state를 관리한다.
    • redux를 사용하면 state를 관리하는 전용 장소(store)에서 상태를 관리하고 컴포넌트는 그걸 보여주기만 하는 용도로 쓰인다.


🕵🏻‍♀️ redux적용 및 개념

action

  • 앱에서 스토어에 운반할 데이터를 말한다. (주문서)
  • Action(액션)은 자바스크립트 객체 형식으로 되어있다.

액션은 무슨 일이 일어났는지 설명해주는 객체이다. 예를 들어 user라는 state를 수정해주는 역할을 하는 액션은 SET_USER와 같이 선언하고, setUser(user)라는 함수 형태의 action creator을 만들어주면 된다.

액션 선언 (대부분 대문자로 선언)

const SET_WINNER = 'SET_WINNER';
const CLICK_CELL = 'CLICK_CELL';
const CHANGE_TURN = 'CHANGE_TURN';

액션을 반환하는 action creators (액션 생성 함수)

export const setWinner(user) {
	return {type: SET_WINNER, user}
}

reducer

  • reducer는 state와 action을 파라미터로 받아와서 state를 변경하는 함수
  • action에는 액션 생성 함수에서 반환하는 객체가 들어간다.
  • return에서 다른 state값은 변경되지 않게 하기 위해 전개 연산자를 사용한다.
  • reducer를 작성할 때 switch문을 자주 사용한다.
const reducer = (state = initState, action) => {
	switch(action.type) {
		case Action이름:
			return {
				...state,
				바꿀 state: 지정 값
			}
	}
}
  • 각 컴포넌트 별로 리듀서를 작성했다면 이를 통합해야 한다. 이때 combineReducers()를 사용한다.
import { combineReducers } from 'redux';
import memberDetail from './memberDetail';
import member from './member';

export default combineReducers({
	memberDetail,
	member,
})

Action(액션)을 Store(스토어)에 바로 전달하는 것이 아니다.

Action(액션)을 Reducer(리듀서)에 전달해야 한다.

Reducer(리듀서)가 주문을 보고 Store(스토어)의 상태를 업데이트하는 것이다.

Action(액션)을 Reducer(리듀서)에 전달하기 위해서는 dispatch() 메소드를 사용해야 한다.

store

Store(스토어)는 상태가 관리되는 오직 하나의 공간이다.

컴포넌트와는 별개로 스토어라는 공간이 있어서 그 스토어 안에 앱 에서 필요한 상태를 담는다.

컴포넌트에서 상태 정보가 필요할 때 스토어에 접근한다.

  • 데이터가 저장되는 가상 공간
  • combineReducers()를 통해 통합된 reducer와 initState(state 초기값)를 준비한다.
import { createStore } from 'redux';
const store = createStore(rootReducer);
  • store을 적용하기 위해서 ‘index.js’를 아래와 같이 수정해주어야 한다.
  • store 옵션을 설정한 태그로 App component를 감싸준다.
import { Provider } from 'react-redux';
import { createStore } from 'redux';
import rootReducer from './store/modules';

const store = createStore(rootReducer);

ReactDOM.render(
	<React.StrictMode>
		<Provider store={store}>
			<App />
		</Provider>
	</React.StrictMode>,
	document.getElementById('root')
);
reportWebVitals()

dispatch

dispatch로 action을 발생 시킨다.

import { setMembersToStore } from '../../store/modules/member';
import { useDispatch } from 'react-redux';

const dispatch = useDispatch();
const saveMembersToStore = data => dispatch(setMembersToStore(data));
...
saveMembersToStore(member);


useSelector, useDispatch로 state에 접근하기

🔹 react-redux

  • yarn add react-redux 또는 npm install react-redux
  • state를 조회하기 위한 useSelector를 사용할 수 있다.
  • action을 발생 시키기 위한 useDispatch를 사용할 수 있다.

🔹 useSelector

  • connect함수를 이용하지 않고 리덕스의 state를 조회할 수 있다.
import { useSelector } from 'react-redux'
const user = useSelector(state => state.user);

🔹 useDispatch

  • 생성한 action을 useDispatch를 통해 발생시킬 수 있다
  • 만들어둔 액션 생성 함수를 import한다.
import { change_user } from '../modules/user'
import { useDispatch } from 'react-redux'

const User = () => {
  ...
  const dispatch = useDispatch();
  dispatch(change_user(user));
  ...
}// 위에서 dispatch한 change_user는 아래와 같이 정의된 액션 생성 함수이다.
export const change_user = createAction(CHANGE_USER, user => user);


redux-actions로 가독성 높이기

🔸 redux-actions

  • 액션 생성 함수를 더 짧은 코드로 작성할 수 있게 해준다.
  • 리듀서를 작성할 때 switch문이 아닌 hendleActions라는 함수를 사용할 수 있게 해준다.

🔸 createAction

  • 액션 생성 함수를 만들어주는 함수
  • 직접 객체를 만들 필요 없어 훨씬 간단하다.

createAction을 사용하지 않았을 때

const CHANGE_USER = 'user/CHANGE_USER';

// 액션 생성 함수
export const change_user = user => ({type: CHANGE_USER, user});

createAction을 사용했을 때

import { createAction } from 'redux-actions';
const CHANGE_USER = 'user/CHANGE_USER';

// 액션 생성 함수
export const chage_user = createAction(CHANGE_USER, user => user);

🔸handleActions

  • handleActions로 리듀서를 더 간단하게 작성할 수 있다.

handleAction을 사용하지 않았을 때

const reducer = (state = initState, action) => {
	switch(action.type) {
		case CHANGE_USER:
			return {
				... state,
				user:action.user
			}
	}
}

handleAction을 사용했을 때

import { handleActions } from 'redux-actions';
const reducer = handleActions({
	[CHANGE_USER]: (state, action) => ({...state, user: action.user})
});

기본 예제

import { createStore } from 'redux'

/**
 * 이것이 (state, action) => state 형태의 순수 함수인 리듀서입니다.
 * 리듀서는 액션이 어떻게 상태를 다음 상태로 변경하는지 서술합니다.
 *
 * 상태의 모양은 당신 마음대로입니다: 기본형(primitive)일수도, 배열일수도, 객체일수도,
 * 심지어 Immutable.js 자료구조일수도 있습니다. 오직 중요한 점은 상태 객체를 변경해서는 안되며,
 * 상태가 바뀐다면 새로운 객체를 반환해야 한다는 것입니다.
 *
 * 이 예제에서 우리는 `switch` 구문과 문자열을 썼지만,
 * 여러분의 프로젝트에 맞게
 * (함수 맵 같은) 다른 컨벤션을 따르셔도 좋습니다.
 */
function counter(state = 0, action) {
  switch (action.type) {
    case 'INCREMENT':
      return state + 1
    case 'DECREMENT':
      return state - 1
    default:
      return state
  }
}

// 앱의 상태를 보관하는 Redux 저장소를 만듭니다.
// API로는 { subscribe, dispatch, getState }가 있습니다.
let store = createStore(counter)

// subscribe()를 이용해 상태 변화에 따라 UI가 변경되게 할 수 있습니다.
// 보통은 subscribe()를 직접 사용하기보다는 뷰 바인딩 라이브러리(예를 들어 React Redux)를 사용합니다.
// 하지만 현재 상태를 localStorage에 영속적으로 저장할 때도 편리합니다.

store.subscribe(() => console.log(store.getState())))

// 내부 상태를 변경하는 유일한 방법은 액션을 보내는 것뿐입니다.
// 액션은 직렬화할수도, 로깅할수도, 저장할수도 있으며 나중에 재실행할수도 있습니다.
store.dispatch({ type: 'INCREMENT' })
// 1
store.dispatch({ type: 'INCREMENT' })
// 2
store.dispatch({ type: 'DECREMENT' })
// 1
profile
하쿠키

0개의 댓글