Redux Toolkit 응용 프로그램에서 논리 분리

Redux 도구 패키지 (지금부터 RTK라고 부른다) 는 Redux 생태계에 대한 커다란 개선이다.RTK는 Redux 논리를 작성하는 방식을 바꾸었고 Redux에 필요한 모든 템플릿 코드를 차단하는 것으로 유명하다.
지난 며칠 동안 나는 이 도서관에서 노는 것을 매우 좋아했지만, 최근에 나는 내가 불쾌한 상황에 처해 있다는 것을 발견했다.API에 대한 비동기 호출을 포함한 모든 Redux 논리는 slice 파일로 포장되어 있습니다. (이따가 슬라이드에 대해 상세하게 설명하겠습니다.)
RTK가 슬라이스 구성을 권장하는 방식임에도 불구하고 애플리케이션이 늘어나면서 파일을 탐색하기 어려워지고 결국 시각적 장애가 되고 말았습니다.

면책 성명


이 글은 RTK나 Redux를 어떻게 사용하는지에 대한 입문 안내서가 아니지만, 나는 RTK의 미세한 차이를 설명하기 위해 최선을 다했다.
React의 상태 관리에 대해 조금만 알면 이 글에서 가치를 얻을 수 있습니다.언제든지 액세스할 수 있습니다docs.

슬라이스


초보자들에게 슬라이스라는 단어는 익숙하지 않은 단어가 될 것이기 때문에 나는 그것이 무엇인지 간략하게 설명할 것이다.RTK에서 슬라이스는 최종적으로 Redux 저장소에 전달된 상태를 저장하는 함수입니다.프로그램의 모든 구성 요소가 접근할 수 있도록 슬라이드에 사용되는 Reducer 함수를 정의하고 내보냅니다.
슬라이스에 포함된 데이터는 다음과 같습니다.
  • 슬라이스의 이름 - Redux 스토리지
  • 에서 참조할 수 있습니다.
  • 감속기
  • initialState
  • 상태 변경을 위한 감속기 기능
  • 외부 요청에 응답하는 extraReducers 매개 변수(예를 들어 아래fetchPosts
  • import { createSlice, createAsyncThunk } from '@reduxjs/toolkit'
    
    const initialState = []
    
    // async function
    export const fetchPosts = createAsyncThunk(
      'counter/fetchPosts',
      async (amount) => {
        const response = await fetch('https://api.backend.com').then((res) => res.json())
        return response.data;
      }
    );
    
    // slice
    export const postSlice = createSlice({
      name: 'posts',
      initialState,
      reducers: {
        addPost: (state, action) => {
          // some logic
        },
      },
    })
    
    export const { addPost } = postSlice.actions
    export default postSlice.reducer
    
    슬라이드의 기본 개요
    간단히 말해서 슬라이스 파일은 RTK 애플리케이션의 원동력입니다.다음 명령을 계속 실행하여 RTK를 포함하는 새로운 React 응용 프로그램을 만듭니다
        npx create-react-app my-app --template redux
    
    코드 편집기에서 프로그램을 열 때, 이 템플릿의 폴더 구조가create react 프로그램과 약간 다르다는 것을 알 수 있습니다.

    다른 점은 Redux 스토어를 포함하는 새 폴더app와 응용 프로그램의 모든 기능을 포함하는 폴더features입니다.features 폴더의 각 하위 폴더는 슬라이스 파일, 슬라이스 사용 구성 요소 및 여기에 포함될 수 있는 스타일 파일과 같은 RTK 응용 프로그램의 특정 기능을 나타냅니다.
    이 생성된 템플릿에는 RTK를 사용하여 기능성 Redux 스토리지를 설정하는 기초 지식과 구성 요소에서 스토리지로 할당하는 방법을 보여주는 예제counter 구성 요소도 포함되어 있습니다.
    실행 npm start 이 구성 요소를 미리 봅니다.

    RTK에서 애플리케이션을 구축하는 방식에서는 각 기능이 완전히 분리되므로 한 디렉토리에서 새로 추가된 기능을 쉽게 찾을 수 있습니다.

    문제.


    검토해보겠습니다counterSlice.js
    import { createAsyncThunk, createSlice } from '@reduxjs/toolkit';
    import { fetchCount } from './counterAPI';
    
    const initialState = {
      value: 0,
      status: 'idle',
    };
    // The function below is called a thunk and allows us to perform async logic. It
    // can be dispatched like a regular action: `dispatch(incrementAsync(10))`. This
    // will call the thunk with the `dispatch` function as the first argument. Async
    // code can then be executed and other actions can be dispatched. Thunks are
    // typically used to make async requests.
    export const incrementAsync = createAsyncThunk(
      'counter/fetchCount',
      async (amount) => {
        const response = await fetchCount(amount);
        return response.data;
      }
    );
    
    export const counterSlice = createSlice({
      name: 'counter',
      initialState,
      // The `reducers` field lets us define reducers and generate associated actions
      reducers: {
        increment: (state) => {
          // Redux Toolkit allows us to write "mutating" logic in reducers. It
          // doesn't actually mutate the state because it uses the Immer library,
          // which detects changes to a "draft state" and produces a brand new
          // immutable state based off those changes
          state.value += 1;
        },
        decrement: (state) => {
          state.value -= 1;
        },
        // Use the PayloadAction type to declare the contents of `action.payload`
        incrementByAmount: (state, action) => {
          state.value += action.payload;
        },
      },
      // The `extraReducers` field lets the slice handle actions defined elsewhere,
      // including actions generated by createAsyncThunk or in other slices.
      extraReducers: (builder) => {
        builder
          .addCase(incrementAsync.pending, (state) => {
            state.status = 'loading';
          })
          .addCase(incrementAsync.fulfilled, (state, action) => {
            state.status = 'idle';
            state.value += action.payload;
          });
      },
    });
    export const { increment, decrement, incrementByAmount } = counterSlice.actions;
    // The function below is called a selector and allows us to select a value from
    // the state. Selectors can also be defined inline where they're used instead of
    // in the slice file. For example: `useSelector((state: RootState) => state.counter.value)`
    export const selectCount = (state) => state.counter.value;
    
    export default counterSlice.reducer;
    
    앞서 언급한 바와 같이, 계수기 구성 요소 상태를 처리하는 데 필요한 모든 논리가 이 파일에 통합되어 있음을 알 수 있습니다.createAsyncThunk, 함수createSlice와 속성extraReducers을 사용한 비동기 호출이 모두 존재합니다.
    애플리케이션이 증가함에 따라 백엔드 API에 더 많은 비동기식 요청이 계속 전송되므로 예상치 못한 상황이 발생하지 않도록 모든 가능한 요청 상태를 처리해야 합니다.
    RTK에서 요청할 수 있는 세 가지 상태는 다음과 같습니다.
  • TBD
  • 완료 및
  • 거부
  • 그 중 하나를 처리하려면 최소한 3줄 코드가 필요하다는 것을 명심하세요.비동기 요청은 최소 9줄이 필요합니다.
    약 10여 개의 비동기 요청이 있을 때 내비게이션 파일이 얼마나 어려운지 상상해 보세요.이것은 나도 하고 싶지 않은 악몽이다.

    솔루션


    슬라이드 파일의 가독성을 높이는 가장 좋은 방법은 모든 비동기 요청을 하나의 단독 파일에 의뢰하고 슬라이드 파일로 가져와 요청의 모든 상태를 처리하는 것이다.
    나는 'thunk' 를 접미사로 이 파일을 명명하는 것을 좋아한다. 마치 슬라이스 파일이 'slice' 를 접미사로 사용하는 것과 같다.
    이 점을 보여주기 위해서 나는 응용 프로그램에 새로운 기능을 추가하여 GitHub API와 상호작용을 할 수 있다.다음은 현재의 구조이다
    특징.
    | 카운터
    |_github
    |_githubSlice.js
    |_Githuthubthunk.js
    Githuthubthunk.js
    import { createAsyncThunk } from '@reduxjs/toolkit'
    
    // API keys
    let githubClientId = process.env.GITHUB_CLIENT_ID
    let githubClientSecret = process.env.GITHUB_CLIENT_SECRET
    
    export const searchUsers = createAsyncThunk(
      'github/searchUsers',
        const res = await fetch(`https://api.github.com/search/users?q=${text}&
          client_id=${githubClientId}&
          client_secret=${githubClientSecret}`).then((res) => res.json())
        return res.items
      }
    )
    
    export const getUser = createAsyncThunk('github/getUser', async (username) => {
      const res = await fetch(`https://api.github.com/users/${username}? 
          client_id=${githubClientId}&
          client-secret=${githubClientSecret}`).then((res) => res.json())
      return res
    })
    
    export const getUserRepos = createAsyncThunk(
      'github/getUserRepos',
      async (username) => {
        const res = await fetch(`https://api.github.com/users/${username}/repos?per_page=5&sort=created:asc&
        client_id=${githubClientId}&
        client-secret=${githubClientSecret}`).then((res) => res.json())
        return res
      }
    )
    
    어떻게 사용하는지createAsyncThunk에 대한 더 많은 정보는 docs를 참고하십시오.
    그런 다음 이러한 비동기 요청을 슬라이스 파일로 가져와 extraReducers에서 처리합니다.
    githubSlice.js
    import { createSlice } from '@reduxjs/toolkit'
    import { searchUsers, getUser, getUserRepos } from './githubThunk'
    
    const initialState = {
      users: [],
      user: {},
      repos: [],
      loading: false,
    }
    
    export const githubSlice = createSlice({
      name: 'github',
      initialState,
      reducers: {
        clearUsers: (state) => {
          state.users = []
          state.loading = false
        },
      },
      extraReducers: {
        // searchUsers
        [searchUsers.pending]: (state) => {
          state.loading = true
        },
        [searchUsers.fulfilled]: (state, { payload }) => {
          state.users = payload
          state.loading = false
        },
        [searchUsers.rejected]: (state) => {
          state.loading = false
        },
        // getUser
        [getUser.pending]: (state) => {
          state.loading = true
        },
        [getUser.fulfilled]: (state, { payload }) => {
          state.user = payload
          state.loading = false
        },
        [getUser.rejected]: (state) => {
          state.loading = false
        },
        // getUserRepos
        [getUserRepos.pending]: (state) => {
          state.loading = true
        },
        [getUserRepos.fulfilled]: (state, { payload }) => {
          state.repos = payload
          state.loading = false
        },
        [getUserRepos.rejected]: (state) => {
          state.loading = false
        },
      },
    })
    
    export const { clearUsers } = githubSlice.actions
    export default githubSlice.reducer
    
    나는 extraReducers 속성이 여전히 좀 무거워 보인다는 것을 인정하지만, 우리는 이렇게 하는 것이 가장 좋다.다행히도, 이것은 일반 Redux 프로그램에서 액션과 Reducer 폴더를 사용하여 논리를 분리하는 방식과 유사합니다.

    스토어에 슬라이스 추가


    사용자가 만든 각 세션은 컨텐츠에 액세스할 수 있도록 Redux 저장소에 추가해야 합니다.이것은github 슬라이드를 App/store.js 에 추가해서 실현할 수 있습니다.
    import { configureStore } from '@reduxjs/toolkit'
    import counterReducer from '../features/counter/counterSlice'
    import githubReducer from './features/github/githubSlice'
    
    export const store = configureStore({
      reducer: {
        counter: counterReducer,
        github: githubReducer,
      },
    })
    
    또 하나 고려해야 할 것은 ExtraReducer에서 요청을 어떻게 처리하는가이다.예시 슬라이드 파일 counterSlice 에서 요청을 처리할 때 서로 다른 문법을 사용했음을 알 수 있습니다.githubSlice에서 나는 extraReducers의 매핑 대상 표시법으로 나의 요구를 처리했다. 주로 이런 방법이 더욱 깔끔하고 쓰기 쉽기 때문이다.
    요청을 처리하는 데 추천하는 방법은 생성기 리셋입니다. 예시 counterSlice.js 파일과 같습니다.더 나은 TypeScript 지원이 있기 때문에, IDE는 자바스크립트 사용자에게도 자동으로 완성됩니다.이 생성기 기호도 슬라이스에 일치하는 감축기와 기본 대소문자 감축기를 추가하는 유일한 방법이다.

    역변성과 불변성


    이 때 RTK에서 상태를 수정하는 방식이 일반 Redux 프로그램이나 React의 상하문 API에서 상태를 수정하는 방식과 차이가 있음을 알 수 있습니다.
    RTK를 사용하면 "변이"구문을 사용하여 더 간단한 불변 업데이트 논리를 작성할 수 있습니다.
    // RTK
    state.users = payload
    
    // Redux
    return {
      ...state,
      users: [...state.users, action.payload]
    }
    
    RTK는 내부 사용Immer library으로 인해 상태가 변경되지 않습니다.Immer는 초안 상태의 변경 사항을 감지하고 변경 사항에 따라 새로운 불변 상태를 생성합니다.
    이렇게 하면 상태 복사본을 수정하여 새로운 데이터를 추가하기 전에 상태 복사본을 만드는 전통적인 방법을 피할 수 있다.Immerhere를 사용하여 변경되지 않는 코드를 작성하는 방법에 대해 자세히 알아보십시오.

    구성 요소의 스케줄링 작업


    두 가지 중요한 연결고리 빌리기;useSelectoruseDispatch가 다른 react-redux라는 라이브러리에서 슬라이스 파일에서 만들 수 있는 모든 구성 요소를 할당할 수 있습니다.
    이 명령을 사용하여 react redux 설치
    npm i react-redux
    
    현재 당신은 useDispatch 갈고리를 사용하여 상점에 조작을 발송할 수 있습니다
    수색하다.js
    import React, { useState } from 'react'
    import { useDispatch } from 'react-redux'
    import { searchUsers } from '../../redux/features/github/githubThunk'
    
    const Search = () => {
      const dispatch = useDispatch()
    
      const [text, setText] = useState('')
    
      const onSubmit = (e) => {
        e.preventDefault()
        if(text !== '') {
          dispatch(searchUsers(text))
          setText('')
        }
      }
    
      const onChange = (e) => setText(e.target.value)
    
      return (
        <div>
          <form className='form' onSubmit={onSubmit}>
            <input
              type='text'
              name='text'
              placeholder='Search Users...'
              value={text}
              onChange={onChange}
            />
            <input
              type='submit'
              value='Search'
            />
          </form>
        </div>
      )
    }
    
    export default Search
    
    요청이 충족되면 Redux 스토리지가 데이터로 채워집니다.

    결론


    Redux 키트는 훌륭한 라이브러리임에 틀림없습니다.그들이 취한 모든 조치와 그 사용이 얼마나 간단한지, 이것은 개발자의 체험을 얼마나 중시하는지 보여준다. 나는 RTK가 Redux를 작성하는 유일한 방법이라고 진심으로 믿는다.
    RTK도 여기서 멈추지 않았다.그들의 팀은 Redux 응용 프로그램에서 데이터를 쉽게 캐시하고 얻는 데 사용되는 RTK 조회를 더욱 개발했다.RTK가 Redux의 현재 상황을 작성하는 것이 되는 것은 시간문제입니다.
    이 접근 방식과 RTK에 대한 전반적인 견해는 어떻습니까?나는 약간의 피드백을 받아서 매우 기쁘다.😄

    좋은 웹페이지 즐겨찾기