Redux

heejung·2022년 6월 30일
0

React

목록 보기
12/13

Redux란?

  • 앱 전체 상태를 쉽게 관리하기 위한 라이브러리
  • Redux의 많은 개념들이 Flux 패턴에서 차용됨
  • 주로 React 앱과 같이 사용됨

핵심 원칙

  1. Single source of truth : Store는 단 하나이며, 모든 앱의 상태는 이곳에 보관됨

  2. Immutability : 상태는 오로지 읽을 수만 있으며, 변경하려면 모든 상태가 변경되어야 함

  3. Pure function : 상태의 변경은 어떠한 side effect도 만들지 않아야 함

Action

  • 상태의 변경을 나타내는 개념
  • 주로 type, payload를 포함하는 자바스크립트 객체
const action1 = {
  type: 'namespace/getMyData',
  payload: {
    id: 123
  }
}

Action Creator

  • Action을 생성하는 함수
  • 직접 Action을 생성하는 것보다 재사용성이 좋음
const addObj = (id) => ({
  type: 'namespace/getMyData',
  payload: {
    id: String(id).slice(1)
  }
})

Store

  • 앱 전체의 상태를 보관하는 곳
  • Action에 따라 reducer에서는 새로운 상태를 만들어내고, Store는 그 상태를 저장함
  • Store의 상태는 불변 => 액션이 발생할 때마다 새로운 객체가 생성됨
const store = createStore(reducer, initialState)

Reducer

  • Action을 받아 새로운 state 생성
  • 상태 변경 시 side effect가 없어야 함
const reducer = (state, action) => {
  switch (action.type) {
    case 'namespace/getMyData':
      const obj = { id: action.payload.id }
      return { ...state, obj }
    default:
      return state
  }
}

const store = createStore(reducer, initialState)

Dispatch

  • Action을 redux로 보내는 함수
  • dispatch 후에 action은 middleware를 거쳐 reducer에 도달함
function MyApp() {
  const dispatch = useDispatch()
  return (
    <button onClick={() => dispatch(addObj(1234)}>Submit</button>
  )
}

Selector

  • 특정 state 조각을 store로부터 가져오는 함수
  • store의 stat는 raw data를 저장하고, 계산된 값 등을 selector로 가져오는 패턴을 구현할 때 유용
function MyApp() {
  const obj = useSelector(state => state.obj)
  return (
    <div>{JSON.stringify(obj)}</div>
  )
}

Redux의 구조


redux는 middleware, enhancer 등을 이용해 자유롭게 확장하여 사용할 수 있다.

  • action은 dispatch 이후 모든 middleware를 먼저 통과한 후 reducer에 도달함
  • middleware => redux-thunk, redux-logger 등의 라이브러리를 적용
  • enhancer => redux devtools 등의 라이브러리를 적용

Redux-toolkit 활용


  • Redux에서 공식적으로 추천하는 helper 라이브러리
  • 기존에 만들어야하는 수많은 boiler plate를 제거하고 유용한 라이브러리를 포함
    (redux-devtools, immerjs, redux-thunk, reselect 등)

redux-toolkit API - configureStore

  • redux의 createStore 함수를 래핑
  • named parameter로 쉽게 store 작성
  • reducer는 객체를 받아 combineReducers를 적용함
const store = configureStore({
  reducer: {
    posts: postsReducer,
    users: usersReducer
  }
})

redux-toolkit API - createAction

  • Action creator를 만드는 함수
  • 생성된 action creator에 데이터를 넘기면 payload 필드로 들어감
  • 생성된 action creator는 자신이 생성하는 액션의 타입 string을 리턴함
const addPost = createAction('post/addPost')

addPost({ title: 'post 1' })

/*
{
	type: 'post/addPost',
    payload: { title: 'post 1' }
}
*/

redux-toolkit API - createReducer

  • reducer 생성
  • builder의 addCase 메소드를 이용 => action마다 state의 변경을 정의
  • immerjs를 내부적으로 사용 => mutable code를 이용해 간편하게 변경 코드 작성
const postsReducer = createReducer(initState, builder => {
  builder.addCase(addPost, (state, action) => {
    state.posts.push(action.payload)
  })
})

redux-toolkit API - createSlice

  • Slice : Action creator, reducer 등 별도로 만들어야하는 여러 Redux 구현체를 하나의 객체로 모은 것
    (많은 boiler plate를 제거할 수 있음)
const postsSlice = createSlice({
  name: 'posts',
  initialState,
  reducers: {
    addPost(state, action) {
      state.posts.push(action.payload)
    }
  }
})

const { addPost } = postsSlice.actions
const reducer = postsSlice.reducer

redux-toolkit API - createSelector

  • state를 이용한 특정 데이터 리턴
  • 내부적으로 데이터를 캐시하며, 데이터 변동이 없다면 캐시된 데이터를 리턴함
const postsSelector = state => state.posts

const userSelector = state => state.user

const postsByUserIdSelector = createSelector(
  postsSelector,
  userSelector,
  (posts, user) =>
  	posts.filter(post => post.username === user.username)
)

Redux를 React에 연결하기


react-redux

  • redux를 react 앱에 연결해주는 라이브러리
  • redux에서 관리하는 상태, dispatch 함수 등을 가져올 수 있음
  • 클래스 컴포넌트, 함수형 컴포넌트에 모두 연결 가능

react-redux API - Provider

  • Redux store를 react와 연결하기위해 Provider로 컴포넌트를 감싸야 함
  • Provider 안에서 렌더링된 컴포넌트들은 state에 접근 가능
const store = configureStore({
  reducer: rootReducer
})

function App() {
  return (
    <Provider store={store}>
    	<MyPage />
    </Provider>
  )
}

react-redux API - useDispatch

  • Redux의 dispatch 함수를 가져오기 위한 API
  • dispatch로 action creator가 생성한 action을 보내면 redux 내부로 전달됨
const addPost = createAction('addPost')

function MyPage() {
  const dispatch = useDispatch()
  
  // dispatch로 redux 내부에 action 전달
  const handleClcik = () => dispatch(addPost())
  
  return (
    <button onClick={handleClick}>Submit</button>
  )
}

react-redux API - useSelector

  • Redux store로부터 데이터를 얻기 위한 API
  • selector function을 인자로 넘김
  • selector function은 데이터에 변경을 가하면 안 됨
  • 데이터를 특정 형태로 계산해 읽을 수 있음
function MyPage() {
  const posts = useSelector(state => state.posts)
  
  return posts.map(post => <Post {...post} />)
}

Redux를 이용한 비동기 처리


  • 비동기를 위한 middleware를 추가해야 함
  • redux-thunk : Promise를 이용한 비동기 Action을 쉽게 처리하는 middleware

createAsyncThunk

  • redux-toolkit에서는 thunk middleware를 기본으로 추가
  • redux-toolkit은 createAsyncThunk API를 제공함
    (fulfilled, rejected, pending 3가지 상태에 대해 각각 reducer를 작성)
// (action type, async callback)을 인자로 받음
const addPost = createAsyncThunk('posts/addPost', async (title) => {
  const result = await PostAPI.addPost({ title })
  return result.data
})

// component
useEffect(() => {
  dispatch(addPost("post 1"))
}, [])

action type이 주어지면 pending, fulfilled, rejected가 각각 postfix로 붙어 reducer로 들어온다.
(ex: posts/addPost/pending)

createAsyncThunk로 생성된 action creator는 4가지 함수로 구성된다.

  • addPost : async 함수를 dispatch 하는 함수
  • addPost.pending : promise 생성했을 때 발생하는 액션
  • addPost.fulfilled : promise가 fulfilled 되었을 때 발생하는 액션
  • addPost.rejected : promise가 rejected 되었을 때 발생하는 액션
profile
프론트엔드 공부 기록

0개의 댓글