Redux-퀵 가이드

Redux는 JavaScript 앱을위한 예측 가능한 상태 컨테이너입니다. 애플리케이션이 성장함에 따라 조직을 유지하고 데이터 흐름을 유지하기가 어려워집니다. Redux는 Store라는 단일 전역 객체로 애플리케이션의 상태를 관리하여이 문제를 해결합니다. Redux 기본 원칙은 애플리케이션 전체에서 일관성을 유지하는 데 도움이되므로 디버깅 및 테스트가 더 쉬워집니다.

더 중요한 것은 시간 여행 디버거와 결합 된 라이브 코드 편집을 제공한다는 것입니다. React, Angular, Vue 등과 같은 모든 뷰 레이어와 함께 사용할 수 있습니다.

Redux의 원리

Redux의 예측 가능성은 다음과 같이 가장 중요한 세 가지 원칙에 의해 결정됩니다.

진실의 단일 소스

전체 애플리케이션의 상태는 단일 저장소 내의 개체 트리에 저장됩니다. 전체 애플리케이션 상태가 단일 트리에 저장되므로 디버깅이 쉽고 개발 속도가 빨라집니다.

상태가 읽기 전용입니다.

상태를 변경하는 유일한 방법은 발생한 일을 설명하는 객체 인 작업을 내보내는 것입니다. 즉, 아무도 애플리케이션의 상태를 직접 변경할 수 없습니다.

순수한 기능으로 변경

상태 트리가 동작에 의해 어떻게 변환되는지 지정하려면 순수 리듀서를 작성합니다. 감속기는 상태 수정이 발생하는 중심 위치입니다. Reducer는 상태와 동작을 인수로 취하고 새로 업데이트 된 상태를 반환하는 함수입니다.

Redux를 설치하기 전에 we have to install Nodejs and NPM. 다음은 설치에 도움이되는 지침입니다. 장치에 Nodejs 및 NPM이 이미 설치되어있는 경우이 단계를 건너 뛸 수 있습니다.

  • 방문 https://nodejs.org/ 패키지 파일을 설치하십시오.

  • 설치 프로그램을 실행하고 지침에 따라 라이센스 계약에 동의하십시오.

  • 실행하려면 장치를 다시 시작하십시오.

  • 명령 프롬프트를 열고 node -v를 입력하여 성공적인 설치를 확인할 수 있습니다. 시스템에있는 최신 버전의 Node가 표시됩니다.

  • npm이 성공적으로 설치되었는지 확인하려면 최신 npm 버전을 반환하는 npm –v를 입력 할 수 있습니다.

redux를 설치하려면 다음 단계를 따르십시오.

명령 프롬프트에서 다음 명령을 실행하여 Redux를 설치하십시오.

npm install --save redux

반응 애플리케이션과 함께 Redux를 사용하려면 다음과 같이 추가 종속성을 설치해야합니다.

npm install --save react-redux

Redux 용 개발자 도구를 설치하려면 다음을 종속성으로 설치해야합니다.

명령 프롬프트에서 아래 명령을 실행하여 Redux dev-tools를 설치하십시오.

npm install --save-dev redux-devtools

Redux 개발 도구를 설치하지 않고 프로젝트에 통합하지 않으려면 다음을 설치할 수 있습니다. Redux DevTools Extension Chrome 및 Firefox 용.

응용 프로그램의 상태가 다음과 같은 일반 객체로 설명된다고 가정 해 보겠습니다. initialState 다음과 같습니다-

const initialState = {
   isLoading: false,
   items: [],
   hasError: false
};

애플리케이션의 모든 코드는이 상태를 변경할 수 없습니다. 상태를 변경하려면 액션을 전달해야합니다.

액션이란 무엇입니까?

작업은 유형 속성으로 변경하려는 의도를 설명하는 일반 개체입니다. 수행중인 작업 유형을 알려주는 유형 속성이 있어야합니다. 행동 명령은 다음과 같습니다-

return {
   type: 'ITEMS_REQUEST', //action type
   isLoading: true //payload information
}

작업과 상태는 Reducer라는 함수에 의해 함께 유지됩니다. 변경을 유발할 의도로 조치가 발송됩니다. 이 변경은 감속기에 의해 수행됩니다. Reducer는 Redux에서 상태를 변경하는 유일한 방법으로, 더 예측 가능하고 중앙 집중화되고 디버깅 가능합니다. 'ITEMS_REQUEST'작업을 처리하는 감속기 함수는 다음과 같습니다.

const reducer = (state = initialState, action) => { //es6 arrow function
   switch (action.type) {
      case 'ITEMS_REQUEST':
         return Object.assign({}, state, {
            isLoading: action.isLoading
         })
      default:
         return state;
   }
}

Redux에는 애플리케이션 상태를 보유하는 단일 저장소가 있습니다. 데이터 처리 로직을 기반으로 코드를 분할하려면 Redux에 저장하는 대신 리듀서 분할을 시작해야합니다.

이 튜토리얼의 뒷부분에서 리듀서를 분할하고이를 저장소와 결합하는 방법에 대해 설명합니다.

Redux 구성 요소는 다음과 같습니다-

Redux는 단방향 데이터 흐름을 따릅니다. 즉, 응용 프로그램 데이터가 단방향 바인딩 데이터 흐름을 따릅니다. 애플리케이션이 성장하고 복잡 해짐에 따라 애플리케이션 상태를 제어 할 수없는 경우 문제를 재현하고 새로운 기능을 추가하기가 어렵습니다.

Redux는 상태 업데이트가 발생할 수있는 방법과시기에 대한 제한을 적용하여 코드의 복잡성을 줄입니다. 이렇게하면 업데이트 된 상태를 쉽게 관리 할 수 ​​있습니다. Redux의 세 가지 원칙으로 제한 사항에 대해 이미 알고 있습니다. 다음 다이어그램은 Redux 데이터 흐름을 더 잘 이해하는 데 도움이됩니다.

  • 사용자가 애플리케이션과 상호 작용할 때 액션이 전달됩니다.

  • 루트 감속기 함수는 현재 상태와 전달 된 작업으로 호출됩니다. 루트 리듀서는 작업을 더 작은 리듀서 함수로 나누어 궁극적으로 새로운 상태를 반환합니다.

  • 스토어는 콜백 함수를 실행하여 뷰에 알립니다.

  • 뷰는 업데이트 된 상태를 검색하고 다시 렌더링 할 수 있습니다.

저장소는 Redux에서 변경할 수없는 개체 트리입니다. 저장소는 애플리케이션의 상태를 보유하는 상태 컨테이너입니다. Redux는 애플리케이션에 하나의 저장소 만 가질 수 있습니다. Redux에서 저장소가 생성 될 때마다 감속기를 지정해야합니다.

다음을 사용하여 상점을 만드는 방법을 살펴 보겠습니다. createStoreRedux의 메소드. 아래와 같이 저장소 생성 프로세스를 지원하는 Redux 라이브러리에서 createStore 패키지를 가져와야합니다.

import { createStore } from 'redux';
import reducer from './reducers/reducer'
const store = createStore(reducer);

createStore 함수에는 세 개의 인수가있을 수 있습니다. 다음은 구문입니다-

createStore(reducer, [preloadedState], [enhancer])

감속기는 앱의 다음 상태를 반환하는 함수입니다. preloadedState는 선택적 인수이며 앱의 초기 상태입니다. 인핸서는 또한 선택적 인수입니다. 타사 기능으로 매장을 개선하는 데 도움이됩니다.

상점에는 아래와 같이 세 가지 중요한 방법이 있습니다.

getState

Redux 스토어의 현재 상태를 검색하는 데 도움이됩니다.

getState의 구문은 다음과 같습니다.

store.getState()

급파

이를 통해 애플리케이션의 상태를 변경하는 작업을 전달할 수 있습니다.

디스패치 구문은 다음과 같습니다.

store.dispatch({type:'ITEMS_REQUEST'})

구독

액션이 전달되었을 때 Redux 스토어가 호출 할 콜백을 등록하는 데 도움이됩니다. Redux 상태가 업데이트되면 뷰가 자동으로 다시 렌더링됩니다.

디스패치 구문은 다음과 같습니다.

store.subscribe(()=>{ console.log(store.getState());})

subscribe 함수는 리스너 구독을 취소하는 함수를 반환합니다. 리스너 구독을 취소하려면 아래 코드를 사용할 수 있습니다.

const unsubscribe = store.subscribe(()=>{console.log(store.getState());});
unsubscribe();

Redux 공식 문서에 따르면 작업은 스토어에 대한 유일한 정보 소스입니다. 애플리케이션에서 저장할 정보의 페이로드를 전달합니다.

앞에서 설명한 것처럼 작업은 수행 된 작업 유형을 나타내는 type 속성이 있어야하는 일반 JavaScript 객체입니다. 무슨 일이 있었는지 알려줍니다. 타입은 아래와 같이 애플리케이션에서 문자열 상수로 정의되어야합니다.

const ITEMS_REQUEST = 'ITEMS_REQUEST';

이 유형 속성과는 별도로 작업 개체의 구조는 전적으로 개발자에게 달려 있습니다. 작업 개체를 가능한 한 가볍게 유지하고 필요한 정보 만 전달하는 것이 좋습니다.

스토어를 변경하려면 먼저 store.dispatch () 함수를 사용하여 액션을 전달해야합니다. 액션 객체는 다음과 같습니다-

{ type: GET_ORDER_STATUS , payload: {orderId,userId } }
{ type: GET_WISHLIST_ITEMS, payload: userId }

액션 크리에이터

액션 생성자는 액션 객체 생성 프로세스를 캡슐화하는 기능입니다. 이 함수는 단순히 액션 인 일반 Js 객체를 반환합니다. 깨끗한 코드 작성을 장려하고 재사용 성을 달성하는 데 도움이됩니다.

액션을 전달할 수있는 액션 크리에이터에 대해 알아 보겠습니다. ‘ITEMS_REQUEST’제품 항목에 대한 요청은 서버의 데이터를 나열합니다. 한편,isLoading 'ITEMS_REQUEST'액션 유형의 감속기에서 상태가 true로 설정되어 항목이로드 중임을 나타내며 데이터가 여전히 서버에서 수신되지 않음을 나타냅니다.

처음에 isLoading 상태는 initialState아무것도로드되지 않는다고 가정하는 개체입니다. 브라우저에서 데이터를 받으면 해당 감속기의 'ITEMS_REQUEST_SUCCESS'액션 유형에서 isLoading 상태가 false로 반환됩니다. 이 상태는 데이터 요청이 켜져있는 동안 페이지에 로더 / 메시지를 표시하는 반응 구성 요소의 소품으로 사용할 수 있습니다. 액션 생성자는 다음과 같습니다-

const ITEMS_REQUEST = ‘ITEMS_REQUEST’ ;
const ITEMS_REQUEST_SUCCESS = ‘ITEMS_REQUEST_SUCCESS’ ;
export function itemsRequest(bool,startIndex,endIndex) {
   let payload = {
      isLoading: bool,
      startIndex,
      endIndex
   }
   return {
      type: ITEMS_REQUEST,
      payload
   }
}
export function itemsRequestSuccess(bool) {
   return {
      type: ITEMS_REQUEST_SUCCESS,
      isLoading: bool,
   }
}

디스패치 함수를 호출하려면 디스패치 함수에 대한 인수로 액션을 전달해야합니다.

dispatch(itemsRequest(true,1, 20));
dispatch(itemsRequestSuccess(false));

store.dispatch ()를 직접 사용하여 액션을 전달할 수 있습니다. 그러나 react-Redux 도우미 메서드를 사용하여 액세스 할 가능성이 더 높습니다.connect(). 당신은 또한 사용할 수 있습니다bindActionCreators() 많은 액션 생성자를 디스패치 기능으로 묶는 방법.

함수는 인수라는 입력을 받아 반환 값으로 알려진 출력을 생성하는 프로세스입니다. 함수는 다음 규칙을 준수하는 경우 순수라고합니다.

  • 함수는 동일한 인수에 대해 동일한 결과를 반환합니다.

  • 평가에는 부작용이 없습니다. 즉, 입력 데이터를 변경하지 않습니다.

  • 지역 및 전역 변수의 변형이 없습니다.

  • 전역 변수처럼 외부 상태에 의존하지 않습니다.

함수에 입력으로 전달 된 값의 두 배를 반환하는 함수의 예를 살펴 보겠습니다. 일반적으로 f (x) => x * 2로 작성됩니다. 인수 값 2를 사용하여 함수를 호출하면 출력은 4, f (2) => 4가됩니다.

다음과 같이 JavaScript로 함수 정의를 작성해 보겠습니다.

const double = x => x*2; // es6 arrow function
console.log(double(2));  // 4

Here, double is a pure function.

Redux의 세 가지 원칙에 따라 순수한 함수, 즉 Redux의 감속기로 변경해야합니다. 이제 감속기가 왜 순수한 함수 여야하는지에 대한 의문이 생깁니다.

유형이 다음과 같은 액션을 전달하려고한다고 가정합니다. 'ADD_TO_CART_SUCCESS' 장바구니에 추가 버튼을 클릭하여 장바구니 애플리케이션에 항목을 추가합니다.

감속기가 아래와 같이 장바구니에 항목을 추가한다고 가정 해 보겠습니다.

const initialState = {
   isAddedToCart: false;
}
const addToCartReducer = (state = initialState, action) => { //es6 arrow function
   switch (action.type) {
      case 'ADD_TO_CART_SUCCESS' :
         state.isAddedToCart = !state.isAddedToCart; //original object altered
         return state;
      default:
         return state;
   }
}
export default addToCartReducer ;

가정 해 봅시다. isAddedToCart 부울 값을 반환하여 항목의 '장바구니에 추가'버튼을 비활성화 할시기를 결정할 수있는 상태 개체의 속성입니다. ‘true or false’. 이것은 사용자가 동일한 제품을 여러 번 추가하는 것을 방지합니다. 이제 새 객체를 반환하는 대신 위와 같이 상태에서 i AddedToCart prop을 변경합니다. 이제 장바구니에 항목을 추가하려고해도 아무 일도 일어나지 않습니다. 장바구니에 추가 버튼이 비활성화되지 않습니다.

이 동작의 이유는 다음과 같습니다.

Redux는 두 객체의 메모리 위치로 이전 객체와 새 객체를 비교합니다. 변경이 발생하면 감속기에서 새 객체를 기대합니다. 또한 변경이 발생하지 않으면 이전 개체를 다시 가져올 것으로 예상합니다. 이 경우 동일합니다. 이러한 이유로 Redux는 아무 일도 일어나지 않았다고 가정합니다.

따라서 감속기가 Redux에서 순수한 기능이어야합니다. 다음은 변경없이 작성하는 방법입니다.

const initialState = {
   isAddedToCart: false;
}
const addToCartReducer = (state = initialState, action) => { //es6 arrow function
   switch (action.type) {
      case 'ADD_TO_CART_SUCCESS' :
         return {
            ...state,
            isAddedToCart: !state.isAddedToCart
         }
      default:
         return state;
   }
}
export default addToCartReducer;

리듀서는 Redux의 순수한 기능입니다. 순수 함수는 예측 가능합니다. Reducer는 Redux에서 상태를 변경하는 유일한 방법입니다. 논리와 계산을 작성할 수있는 유일한 곳입니다. Reducer 함수는 전달되는 앱 및 작업의 이전 상태를 수락하고 다음 상태를 계산하고 새 개체를 반환합니다.

감속기 내부에서 다음 몇 가지 작업을 수행해서는 안됩니다.

  • 함수 인수의 변형
  • API 호출 및 라우팅 로직
  • 순수하지 않은 함수 호출, 예 : Math.random ()

다음은 감속기의 구문입니다-

(state,action) => newState

액션 생성자 모듈에서 논의 된 웹 페이지에 제품 항목 목록을 표시하는 예제를 계속해 보겠습니다. 감속기를 작성하는 방법을 아래에서 보겠습니다.

const initialState = {
   isLoading: false,
   items: []
};
const reducer = (state = initialState, action) => {
   switch (action.type) {
      case 'ITEMS_REQUEST':
         return Object.assign({}, state, {
            isLoading: action.payload.isLoading
         })
      case ‘ITEMS_REQUEST_SUCCESS':
         return Object.assign({}, state, {
            items: state.items.concat(action.items),
            isLoading: action.isLoading
         })
      default:
         return state;
   }
}
export default reducer;

첫째, state를 'initialState'로 설정하지 않으면 Redux는 정의되지 않은 상태로 reducer를 호출합니다. 이 코드 예제에서는 기존 배열을 변경하지 않는 'ITEMS_REQUEST_SUCCESS'에서 JavaScript의 concat () 함수를 사용합니다. 대신 새 배열을 반환합니다.

이런 식으로 상태의 변이를 피할 수 있습니다. 주에 직접 쓰지 마십시오. 'ITEMS_REQUEST'에서는 수신 된 액션의 상태 값을 설정해야합니다.

리듀서에서 로직을 작성하고 논리적 데이터 기반으로 분할 할 수 있다는 것은 이미 논의되었습니다. 리듀서를 분할하여 대규모 애플리케이션을 다룰 때 루트 리듀서로 결합하는 방법을 살펴 보겠습니다.

사용자가 제품 주문 상태에 액세스하고 위시리스트 정보를 볼 수있는 웹 페이지를 디자인한다고 가정 해 보겠습니다. 다른 리듀서 파일에서 로직을 분리하여 독립적으로 작동하도록 만들 수 있습니다. 일부 주문 ID 및 사용자 ID에 해당하는 주문 상태를 가져 오기 위해 GET_ORDER_STATUS 액션이 전달되었다고 가정 해 보겠습니다.

/reducer/orderStatusReducer.js
import { GET_ORDER_STATUS } from ‘../constants/appConstant’;
export default function (state = {} , action) {
   switch(action.type) {
      case GET_ORDER_STATUS:
         return { ...state, orderStatusData: action.payload.orderStatus };
      default:
         return state;
   }
}

마찬가지로, GET_WISHLIST_ITEMS 액션이 사용자 각각의 위시리스트 정보를 얻기 위해 전달되었다고 가정합니다.

/reducer/getWishlistDataReducer.js
import { GET_WISHLIST_ITEMS } from ‘../constants/appConstant’;
export default function (state = {}, action) {
   switch(action.type) {
      case GET_WISHLIST_ITEMS:
         return { ...state, wishlistData: action.payload.wishlistData };
      default:
         return state;
   }
}

이제 Redux combineReducers 유틸리티를 사용하여 두 감속기를 결합 할 수 있습니다. combineReducers는 값이 다른 감속기 함수 인 객체를 반환하는 함수를 생성합니다. 인덱스 감속기 파일의 모든 감속기를 가져 와서 각각의 이름을 가진 객체로 결합 할 수 있습니다.

/reducer/index.js
import { combineReducers } from ‘redux’;
import OrderStatusReducer from ‘./orderStatusReducer’;
import GetWishlistDataReducer from ‘./getWishlistDataReducer’;

const rootReducer = combineReducers ({
   orderStatusReducer: OrderStatusReducer,
   getWishlistDataReducer: GetWishlistDataReducer
});
export default rootReducer;

이제이 rootReducer를 다음과 같이 createStore 메소드에 전달할 수 있습니다.

const store = createStore(rootReducer);

Redux 자체는 동기식이므로 async 같은 작업 network requestRedux와 함께 일하십니까? 여기서 미들웨어가 편리합니다. 앞서 논의했듯이 리듀서는 모든 실행 로직이 작성되는 곳입니다. Reducer는 작업을 수행하는 사람, 작업이 전달되기 전과 후에 앱의 상태를 기록하는 데 걸리는 시간과 관련이 없습니다.

이 경우 Redux 미들웨어 기능은 리듀서에 도달하기 전에 디스패치 된 액션과 상호 작용할 수있는 매체를 제공합니다. 사용자 정의 된 미들웨어 함수는 일부 논리를 감싸는 고차 함수 (다른 함수를 반환하는 함수)를 작성하여 만들 수 있습니다. 여러 미들웨어를 함께 결합하여 새로운 기능을 추가 할 수 있으며, 각 미들웨어는 이전과 이후에 무엇이 왔는지 알 필요가 없습니다. 액션 디스패치와 리듀서 사이 어딘가에 미들웨어를 상상할 수 있습니다.

일반적으로 미들웨어는 앱에서 비동기 작업을 처리하는 데 사용됩니다. Redux는 사용자 지정 미들웨어와 redux-thunk 및 redux-promise와 같은 Redux 미들웨어를 사용할 수있는 applyMiddleware라는 API를 제공합니다. 스토어에 미들웨어를 적용합니다. applyMiddleware API를 사용하는 구문은 다음과 같습니다.

applyMiddleware(...middleware)

그리고 이것은 다음과 같이 저장에 적용될 수 있습니다.

import { createStore, applyMiddleware } from 'redux';
import thunk from 'redux-thunk';
import rootReducer from './reducers/index';
const store = createStore(rootReducer, applyMiddleware(thunk));

미들웨어를 사용하면 액션 객체 대신 함수를 반환하는 액션 디스패처를 작성할 수 있습니다. 동일한 예가 아래에 나와 있습니다.

function getUser() {
   return function() {
      return axios.get('/get_user_details');
   };
}

조건부 디스패치는 미들웨어 내부에 작성 될 수 있습니다. 각 미들웨어는 새로운 액션을 디스패치 할 수 있도록 스토어의 디스패치를 ​​수신하고, 현재 상태에 액세스하고 함수를 반환 할 수 있도록 getState 함수를 인수로받습니다. 내부 함수의 반환 값은 디스패치 함수 자체의 값으로 사용할 수 있습니다.

다음은 미들웨어의 구문입니다-

({ getState, dispatch }) => next => action

getState 함수는 현재 상태에 따라 새 데이터를 가져올 지 아니면 캐시 결과를 반환 할지를 결정하는 데 유용합니다.

커스텀 미들웨어 로거 기능의 예를 보겠습니다. 단순히 작업과 새 상태를 기록합니다.

import { createStore, applyMiddleware } from 'redux'
import userLogin from './reducers'

function logger({ getState }) {
   return next => action => {
      console.log(‘action’, action);
      const returnVal = next(action);
      console.log('state when action is dispatched', getState()); 
      return returnVal;
   }
}

이제 다음 코드 줄을 작성하여 로거 미들웨어를 상점에 적용하십시오.

const store = createStore(userLogin , initialState=[ ] , applyMiddleware(logger));

아래 코드를 사용하여 발송 된 액션과 새로운 상태를 확인하는 액션을 발송합니다.

store.dispatch({
   type: 'ITEMS_REQUEST', 
	isLoading: true
})

로더를 표시하거나 숨길 때 처리 할 수있는 미들웨어의 또 다른 예는 다음과 같습니다. 이 미들웨어는 리소스를 요청할 때 로더를 표시하고 리소스 요청이 완료되면이를 숨 깁니다.

import isPromise from 'is-promise';

function loaderHandler({ dispatch }) {
   return next => action => {
      if (isPromise(action)) {
         dispatch({ type: 'SHOW_LOADER' });
         action
            .then(() => dispatch({ type: 'HIDE_LOADER' }))
            .catch(() => dispatch({ type: 'HIDE_LOADER' }));
      }
      return next(action);
   };
}
const store = createStore(
   userLogin , initialState = [ ] , 
   applyMiddleware(loaderHandler)
);

Redux-Devtools는 Redux 앱을위한 디버깅 플랫폼을 제공합니다. 이를 통해 시간 여행 디버깅 및 라이브 편집을 수행 할 수 있습니다. 공식 문서의 일부 기능은 다음과 같습니다.

  • 모든 상태 및 작업 페이로드를 검사 할 수 있습니다.

  • 작업을 "취소"하여 시간을 되돌릴 수 있습니다.

  • 감속기 코드를 변경하면 각 "단계별"작업이 재평가됩니다.

  • 감속기가 발생하면 오류를 식별 할 수 있으며 어떤 조치가 발생했는지 확인할 수 있습니다.

  • persistState () 저장소 향상기를 사용하면 페이지를 다시로드하는 동안 디버그 세션을 유지할 수 있습니다.

아래에 주어진 Redux 개발 도구에는 두 가지 변형이 있습니다.

Redux DevTools − 패키지로 설치하고 아래와 같이 애플리케이션에 통합 할 수 있습니다 −

https://github.com/reduxjs/redux-devtools/blob/master/docs/Walkthrough.md#manual-integration

Redux DevTools Extension − Redux를위한 동일한 개발자 도구를 구현하는 브라우저 확장은 다음과 같습니다 −

https://github.com/zalmoxisus/redux-devtools-extension

이제 Redux 개발 도구를 사용하여 작업을 건너 뛰고 시간을 거슬러 올라가는 방법을 확인하겠습니다. 다음 스크린 샷은 항목 목록을 가져 오기 위해 이전에 발송 한 작업에 대해 설명합니다. 여기에서 검사기 탭에서 전달 된 작업을 볼 수 있습니다. 오른쪽에는 상태 트리의 차이점을 보여주는 데모 탭이 있습니다.

이 도구를 사용하기 시작하면이 도구에 익숙해 질 것입니다. 이 Redux 플러그인 도구에서 실제 코드를 작성하지 않고도 액션을 전달할 수 있습니다. 마지막 행의 Dispatcher 옵션이 도움이 될 것입니다. 항목을 성공적으로 가져 오는 마지막 작업을 확인하겠습니다.

서버에서 응답으로 객체 배열을 받았습니다. 모든 데이터는 우리 페이지에 목록을 표시하는 데 사용할 수 있습니다. 오른쪽 상단의 상태 탭을 클릭하여 상점의 상태를 동시에 추적 할 수도 있습니다.

이전 섹션에서는 시간 여행 디버깅에 대해 배웠습니다. 이제 하나의 작업을 건너 뛰고 시간을 거슬러 올라가 앱의 상태를 분석하는 방법을 확인하겠습니다. 작업 유형을 클릭하면 '점프'와 '건너 뛰기'의 두 가지 옵션이 나타납니다.

특정 작업 유형에서 건너 뛰기 버튼을 클릭하면 특정 작업을 건너 뛸 수 있습니다. 조치가 발생하지 않은 것처럼 작동합니다. 특정 동작 유형에서 점프 버튼을 클릭하면 해당 동작이 발생한 시점으로 이동하고 나머지 동작은 모두 순차적으로 건너 뜁니다. 이렇게하면 특정 작업이 발생했을 때 상태를 유지할 수 있습니다. 이 기능은 응용 프로그램에서 오류를 디버깅하고 찾는 데 유용합니다.

마지막 작업을 건너 뛰었고 백그라운드의 모든 목록 데이터가 사라졌습니다. 항목의 데이터가 도착하지 않은 시간으로 돌아가서 앱에 페이지에 렌더링 할 데이터가 없습니다. 실제로 코딩과 디버깅이 더 쉬워집니다.

Redux 코드 테스트는 우리가 대부분 함수를 작성하기 때문에 쉽고 대부분 순수합니다. 그래서 우리는 그들을 조롱하지 않고도 테스트 할 수 있습니다. 여기서는 JEST를 테스트 엔진으로 사용하고 있습니다. 노드 환경에서 작동하며 DOM에 액세스하지 않습니다.

아래 코드로 JEST를 설치할 수 있습니다.

npm install --save-dev jest

바벨을 사용하면 babel-jest 다음과 같이-

npm install --save-dev babel-jest

그리고 다음과 같이 .babelrc 파일에서 babel-preset-env 기능을 사용하도록 구성하십시오.

{ 
   "presets": ["@babel/preset-env"] 
}
And add the following script in your package.json:
{ 
   //Some other code 
   "scripts": {
      //code
      "test": "jest", 
      "test:watch": "npm test -- --watch" 
   }, 
   //code 
}

드디어, run npm test or npm run test. 액션 생성자와 리듀서를위한 테스트 케이스를 어떻게 작성할 수 있는지 확인해 보겠습니다.

액션 크리에이터를위한 테스트 케이스

아래와 같이 액션 크리에이터가 있다고 가정 해 보겠습니다.

export function itemsRequestSuccess(bool) {
   return {
      type: ITEMS_REQUEST_SUCCESS,
      isLoading: bool,
   }
}

이 액션 생성자는 아래와 같이 테스트 할 수 있습니다.

import * as action from '../actions/actions';
import * as types from '../../constants/ActionTypes';

describe('actions', () => {
   it('should create an action to check if item is loading', () => { 
      const isLoading = true, 
      const expectedAction = { 
         type: types.ITEMS_REQUEST_SUCCESS, isLoading 
      } 
      expect(actions.itemsRequestSuccess(isLoading)).toEqual(expectedAction) 
   })
})

감속기에 대한 테스트 사례

우리는 감속기가 액션이 적용될 때 새로운 상태를 반환해야한다는 것을 배웠습니다. 따라서 감속기는이 동작에 대해 테스트됩니다.

아래에 주어진 감속기를 고려하십시오-

const initialState = {
   isLoading: false
};
const reducer = (state = initialState, action) => {
   switch (action.type) {
      case 'ITEMS_REQUEST':
         return Object.assign({}, state, {
            isLoading: action.payload.isLoading
         })
      default:
         return state;
   }
}
export default reducer;

위의 감속기를 테스트하려면 감속기에 상태와 동작을 전달하고 아래와 같이 새로운 상태를 반환해야합니다.

import reducer from '../../reducer/reducer' 
import * as types from '../../constants/ActionTypes'

describe('reducer initial state', () => {
   it('should return the initial state', () => {
      expect(reducer(undefined, {})).toEqual([
         {
            isLoading: false,
         }
      ])
   })
   it('should handle ITEMS_REQUEST', () => {
      expect(
         reducer(
            {
               isLoading: false,
            },
            {
               type: types.ITEMS_REQUEST,
               payload: { isLoading: true }
            }
         )
      ).toEqual({
         isLoading: true
      })
   })
})

테스트 케이스 작성에 익숙하지 않다면 JEST 기본 사항을 확인할 수 있습니다 .

이전 장에서 우리는 Redux가 무엇이며 어떻게 작동하는지 배웠습니다. 이제 뷰 파트와 Redux의 통합을 확인하겠습니다. Redux에 모든 뷰 레이어를 추가 할 수 있습니다. 리 액트 라이브러리와 Redux에 대해서도 논의 할 것입니다.

다양한 반응 구성 요소가 최상위 구성 요소에서 아래로 내려가는 모든 구성 요소에 소품으로 전달하지 않고 동일한 데이터를 다른 방식으로 표시해야하는 경우를 가정 해 보겠습니다. 반응 구성 요소 외부에 저장하는 것이 이상적입니다. 다른 구성 요소로 데이터를 전달할 필요가 없기 때문에 더 빠른 데이터 검색에 도움이되기 때문입니다.

Redux로 어떻게 가능한지 논의하겠습니다. Redux는 react-redux 패키지를 제공하여 아래와 같이 두 가지 유틸리티로 반응 구성 요소를 바인딩합니다.

  • Provider
  • Connect

공급자는 응용 프로그램의 나머지 부분에서 스토어를 사용할 수 있도록합니다. Connect 기능은 매장 상태에서 발생하는 각 변경 사항에 응답하여 구성 요소를 매장에 연결하는 데 도움이됩니다.

살펴 보겠습니다. root index.js 저장소를 만들고 react-redux 앱의 나머지 앱에 대한 저장소를 활성화하는 공급자를 사용하는 파일입니다.

import React from 'react'
import { render } from 'react-dom'
import { Provider } from 'react-redux'
import { createStore, applyMiddleware } from 'redux';
import reducer from './reducers/reducer'
import thunk from 'redux-thunk';
import App from './components/app'
import './index.css';

const store = createStore(
   reducer,
   window.__REDUX_DEVTOOLS_EXTENSION__ && window.__REDUX_DEVTOOLS_EXTENSION__(),
   applyMiddleware(thunk)
)
render(
   <Provider store = {store}>
      <App />
   </Provider>,
   document.getElementById('root')
)

react-redux 앱에서 변경이 발생할 때마다 mapStateToProps ()가 호출됩니다. 이 함수에서는 반응 컴포넌트에 제공해야하는 상태를 정확하게 지정합니다.

아래에 설명 된 connect () 함수의 도움으로 이러한 앱의 상태를 반응 구성 요소에 연결합니다. Connect ()는 구성 요소를 매개 변수로 사용하는 고차 함수입니다. 특정 작업을 수행하고 마지막으로 내 보낸 올바른 데이터가 포함 된 새 구성 요소를 반환합니다.

mapStateToProps ()의 도움으로 이러한 저장소 상태를 반응 구성 요소에 소품으로 제공합니다. 이 코드는 컨테이너 구성 요소에 래핑 될 수 있습니다. 동기는 데이터 가져 오기, 렌더링 문제 및 재사용 가능성과 같은 문제를 분리하는 것입니다.

import { connect } from 'react-redux'
import Listing from '../components/listing/Listing' //react component
import makeApiCall from '../services/services' //component to make api call

const mapStateToProps = (state) => {
   return {
      items: state.items,
      isLoading: state.isLoading
   };
};
const mapDispatchToProps = (dispatch) => {
   return {
      fetchData: () => dispatch(makeApiCall())
   };
};
export default connect(mapStateToProps, mapDispatchToProps)(Listing);

services.js 파일에서 API 호출을 수행하는 컴포넌트의 정의는 다음과 같습니다.

import axios from 'axios'
import { itemsLoading, itemsFetchDataSuccess } from '../actions/actions'

export default function makeApiCall() {
   return (dispatch) => {
      dispatch(itemsLoading(true));
      axios.get('http://api.tvmaze.com/shows')
      .then((response) => {
         if (response.status !== 200) {
            throw Error(response.statusText);
         }
         dispatch(itemsLoading(false));
         return response;
      })
      .then((response) => dispatch(itemsFetchDataSuccess(response.data)))
   };
}

mapDispatchToProps () 함수는 디스패치 함수를 매개 변수로 받고 반응 컴포넌트에 전달하는 일반 객체로 콜백 소품을 반환합니다.

여기에서 반응 목록 구성 요소의 소품으로 fetchData에 액세스하여 API 호출을 수행하는 작업을 전달할 수 있습니다. mapDispatchToProps ()는 저장할 액션을 전달하는 데 사용됩니다. react-redux에서 구성 요소는 저장소에 직접 액세스 할 수 없습니다. 유일한 방법은 connect ()를 사용하는 것입니다.

아래 다이어그램을 통해 react-redux가 어떻게 작동하는지 이해합시다.

STORE − 모든 애플리케이션 상태를 JavaScript 객체로 저장

PROVIDER − 매장 이용 가능

CONTAINER − 앱 상태 가져 오기 및 구성 요소에 소품으로 제공

COMPONENT − 사용자는 뷰 컴포넌트를 통해 상호 작용합니다.

ACTIONS − 스토어 변경, 앱 상태 변경 여부 변경

REDUCER − 앱 상태를 변경하고, 상태 및 작업을 수락하고, 업데이트 된 상태를 반환하는 유일한 방법입니다.

그러나 Redux는 독립적 인 라이브러리이며 모든 UI 레이어와 함께 사용할 수 있습니다. React-redux는 반응과의 UI 바인딩 인 공식 Redux입니다. 또한 좋은 반응 Redux 앱 구조를 장려합니다. React-redux는 내부적으로 성능 최적화를 구현하므로 필요할 때만 구성 요소를 다시 렌더링합니다.

요약하자면 Redux는 가장 짧고 빠른 코드를 작성하도록 설계되지 않았습니다. 예측 가능한 상태 관리 컨테이너를 제공하기위한 것입니다. 특정 상태가 언제 변경되었는지 또는 데이터의 출처를 이해하는 데 도움이됩니다.

다음은 react 및 Redux 애플리케이션의 작은 예입니다. 작은 앱을 개발해 볼 수도 있습니다. 증가 또는 감소 카운터에 대한 샘플 코드는 다음과 같습니다.

이것은 저장소 생성 및 반응 앱 구성 요소 렌더링을 담당하는 루트 파일입니다.

/src/index.js

import React from 'react'
import { render } from 'react-dom'
import { Provider } from 'react-redux'
import { createStore } from 'redux';
import reducer from '../src/reducer/index'
import App from '../src/App'
import './index.css';

const store = createStore(
   reducer,
   window.__REDUX_DEVTOOLS_EXTENSION__ && 
   window.__REDUX_DEVTOOLS_EXTENSION__()
)
render(
   <Provider store = {store}>
      <App />
   </Provider>, document.getElementById('root')
)

이것이 반응의 근본 구성 요소입니다. 카운터 컨테이너 구성 요소를 자식으로 렌더링하는 역할을합니다.

/src/app.js

import React, { Component } from 'react';
import './App.css';
import Counter from '../src/container/appContainer';

class App extends Component {
   render() {
      return (
         <div className = "App">
            <header className = "App-header">
               <Counter/>
            </header>
         </div>
      );
   }
}
export default App;

다음은 Redux의 상태를 반응 구성 요소에 제공하는 컨테이너 구성 요소입니다.

/container/counterContainer.js

import { connect } from 'react-redux'
import Counter from '../component/counter'
import { increment, decrement, reset } from '../actions';

const mapStateToProps = (state) => {
   return {
      counter: state
   };
};
const mapDispatchToProps = (dispatch) => {
   return {
      increment: () => dispatch(increment()),
      decrement: () => dispatch(decrement()),
      reset: () => dispatch(reset())
   };
};
export default connect(mapStateToProps, mapDispatchToProps)(Counter);

아래에 뷰 부분을 담당하는 반응 구성 요소가 있습니다.

/component/counter.js
import React, { Component } from 'react';
class Counter extends Component {
   render() {
      const {counter,increment,decrement,reset} = this.props;
      return (
         <div className = "App">
            <div>{counter}</div>
            <div>
               <button onClick = {increment}>INCREMENT BY 1</button>
            </div>
            <div>
               <button onClick = {decrement}>DECREMENT BY 1</button>
            </div>
            <button onClick = {reset}>RESET</button>
         </div>
      );
   }
}
export default Counter;

다음은 액션 생성을 담당하는 액션 제작자입니다.

/actions/index.js
export function increment() {
   return {
      type: 'INCREMENT'
   }
}
export function decrement() {
   return {
      type: 'DECREMENT'
   }
}
export function reset() {
   return { type: 'RESET' }
}

아래에는 Redux에서 상태를 업데이트하는 감속기 파일에 대한 코드 줄이 나와 있습니다.

reducer/index.js
const reducer = (state = 0, action) => {
   switch (action.type) {
      case 'INCREMENT': return state + 1
      case 'DECREMENT': return state - 1
      case 'RESET' : return 0 default: return state
   }
}
export default reducer;

처음에 앱은 다음과 같이 보입니다.

증분을 두 번 클릭하면 출력 화면은 다음과 같습니다.

한 번 감소하면 다음 화면이 표시됩니다.

그리고 리셋하면 앱이 카운터 값 0 인 초기 상태로 돌아갑니다. 이것은 다음과 같습니다.

첫 번째 증분 작업이 발생할 때 Redux 개발 도구에서 어떤 일이 발생하는지 이해하겠습니다.

앱 상태는 증가 작업 만 전달되고 나머지 작업은 건너 뛰는 시간으로 이동됩니다.

우리는 혼자서 과제로 작은 Todo 앱을 개발하고 Redux 도구를 더 잘 이해하도록 권장합니다.