React Native에서 Redux, RxJS 및 Redux-Observable을 사용하여 반응형 앱 빌드

게시 됨: 2022-03-11

풍부하고 강력한 웹 및 모바일 앱의 성장하는 생태계에서 현재 사용자, 로드된 항목 목록, 로드 상태, 오류 등과 같이 관리해야 할 상태가 점점 더 많아지고 있습니다. Redux는 상태를 전역 객체에 유지함으로써 이 문제에 대한 한 가지 솔루션입니다.

Redux의 한계 중 하나는 기본적으로 비동기 동작을 지원하지 않는다는 것입니다. 이에 대한 한 가지 솔루션은 JavaScript에서 반응형 프로그래밍을 위한 강력한 라이브러리인 RxJS를 기반으로 하는 redux-observable 입니다. RxJS는 Microsoft에서 시작된 리액티브 프로그래밍용 API인 ReactiveX의 구현입니다. ReactiveX는 반응 패러다임, 함수형 프로그래밍, 관찰자 ​​패턴 및 반복자 패턴의 가장 강력한 기능 중 일부를 결합합니다.

이 튜토리얼에서는 Redux와 React에서의 사용법에 대해 배울 것입니다. 또한 RxJS를 사용한 반응형 프로그래밍과 이것이 어떻게 지루하고 복잡한 비동기 작업을 매우 간단하게 만들 수 있는지 살펴보겠습니다.

마지막으로 RxJS를 활용하여 비동기 작업을 수행하는 라이브러리인 redux-observable을 학습한 다음 Redux 및 redux-observable을 사용하여 React Native에서 애플리케이션을 빌드합니다.

리덕스

GitHub에 설명되어 있듯이 Redux는 "JavaScript 앱을 위한 예측 가능한 상태 컨테이너"입니다. JavaScript 앱에 전역 상태를 제공하여 상태 및 작업을 React 구성 요소에서 멀리 유지합니다.

Redux가 없는 일반적인 React 애플리케이션에서는 속성 또는 props 를 통해 루트 노드에서 자식으로 데이터를 전달해야 합니다. 이 데이터 흐름은 소규모 애플리케이션에서 관리할 수 있지만 애플리케이션이 성장함에 따라 매우 복잡해질 수 있습니다. Redux를 사용하면 구성 요소를 서로 독립적으로 사용할 수 있으므로 단일 정보 소스로 사용할 수 있습니다.

Redux는 React 구성 요소가 Redux에서 데이터를 읽고 Redux 상태를 업데이트하기 위한 작업을 디스패치하는 바인딩을 제공하는 react-redux 를 사용하여 React에서 사용할 수 있습니다.

리덕스

Redux는 세 가지 간단한 원칙으로 설명할 수 있습니다.

1. 진실의 단일 소스

전체 애플리케이션의 상태는 단일 개체에 저장됩니다. Redux의 이 객체는 상점에서 보유합니다. 모든 Redux 앱에는 단일 저장소가 있어야 합니다.

 » console.log(store.getState()) « { user: {...}, todos: {...} }

React 구성 요소의 Redux에서 데이터를 읽으려면 react-reduxconnect 기능을 사용합니다. connect 는 4개의 인수를 취하며 모두 선택 사항입니다. 지금은 mapStateToProps 라는 첫 번째 항목에 집중하겠습니다.

 /* UserTile.js */ import { connect } from 'react-redux'; class UserTile extends React.Component { render() { return <p>{ this.props.user.name }</p> } } function mapStateToProps(state) { return { user: state.user } } export default connect(mapStateToProps)(UserTile)

위의 예에서 mapStateToProps 는 전역 Redux 상태를 첫 번째 인수로 수신하고 상위 구성 요소에 의해 <UserTile /> 에 전달된 props와 병합될 개체를 반환합니다.

2. 상태가 읽기 전용입니다.

Redux 상태는 React 구성 요소에 대해 읽기 전용이며 상태를 변경하는 유일한 방법은 action 을 내보내는 것입니다. 액션은 상태를 변경하려는 의도를 나타내는 일반 객체입니다. 모든 작업 개체에는 type 필드가 있어야 하고 값은 문자열이어야 합니다. 그 외에 작업의 내용은 전적으로 사용자에게 달려 있지만 대부분의 앱은 플럭스 표준 작업 형식을 따르므로 작업 구조를 4개의 키로 제한합니다.

  1. type 작업에 대한 임의의 문자열 식별자입니다. 모든 작업에는 고유한 작업이 있어야 합니다.
  2. payload 모든 작업에 대한 선택적 데이터입니다. 언제든지 가능하며 작업에 대한 정보를 포함합니다.
  3. error 조치가 오류를 나타내는 경우 true로 설정된 선택적 부울 속성입니다. 이것은 거부된 Promise. string 작업의 Promise. string 식별자입니다. 모든 작업에는 고유한 작업이 있어야 합니다. 관례에 따라 errortrue payload 는 오류 객체여야 합니다.
  4. meta 메타는 모든 유형의 값이 될 수 있습니다. 페이로드의 일부가 아닌 추가 정보를 위한 것입니다.

다음은 작업의 두 가지 예입니다.

 store.dispatch({ type: 'GET_USER', payload: '21', }); store.dispatch({ type: 'GET_USER_SUCCESS', payload: { user: { id: '21', name: 'Foo' } } });

3. 순수 함수로 상태 변경

전역 Redux 상태는 리듀서라고 하는 순수 함수를 사용하여 변경됩니다. 리듀서는 이전 상태와 동작을 취하고 다음 상태를 반환합니다. 감속기는 기존 상태를 변경하는 대신 새 상태 개체를 만듭니다. 앱 크기에 따라 Redux 스토어에는 단일 리듀서 또는 여러 리듀서가 있을 수 있습니다.

 /* store.js */ import { combineReducers, createStore } from 'redux' function user(state = {}, action) { switch (action.type) { case 'GET_USER_SUCCESS': return action.payload.user default: return state } } function todos(state = [], action) { switch (action.type) { case 'ADD_TODO_SUCCESS': return [ ...state, { id: uuid(), // a random uuid generator function text: action.text, completed: false } ] case 'COMPLETE_TODO_SUCCESS': return state.map(todo => { if (todo.id === action.id) { return { ...todo, completed: true } } return todo }) default: return state } } const rootReducer = combineReducers({ user, todos }) const store = createStore(rootReducer)

상태에서 읽는 것과 유사하게 connect 기능을 사용하여 작업을 전달할 수 있습니다.

 /* UserProfile.js */ class Profile extends React.Component { handleSave(user) { this.props.updateUser(user); } } function mapDispatchToProps(dispatch) { return ({ updateUser: (user) => dispatch({ type: 'GET_USER_SUCCESS', user, }), }) } export default connect(mapStateToProps, mapDispatchToProps)(Profile);

RxJS

RxJS

반응형 프로그래밍

반응형 프로그래밍은 " 스트림 "의 데이터 흐름과 전파 및 변경 사항을 처리하는 선언적 프로그래밍 패러다임입니다. 자바스크립트의 반응형 프로그래밍을 위한 라이브러리인 RxJS는 관찰자가 구독 할 수 있는 데이터 스트림인 관찰 가능한 개념을 가지고 있으며, 이 관찰자는 시간이 지남에 따라 데이터를 전달받습니다.

옵저버블의 옵저버는 next , error , complete 의 세 가지 기능을 가진 객체입니다. 이러한 기능은 모두 선택 사항입니다.

 observable.subscribe({ next: value => console.log(`Value is ${value}`), error: err => console.log(err), complete: () => console.log(`Completed`), })

.subscribe 함수는 객체 대신 세 가지 함수를 가질 수도 있습니다.

 observable.subscribe( value => console.log(`Value is ${value}`), err => console.log(err), () => console.log(`Completed`) )

Observable의 객체를 생성하여 새로운 observable 을 생성할 수 있으며, Observer라고도 불리는 구독자를 수신하는 함수를 전달합니다. 구독자에는 세 가지 메서드가 있습니다. next , errorcomplete . 구독자는 필요한 만큼 값을 사용하여 next를 호출할 수 있으며 결국 complete 되거나 error 합니다. complete 또는 error 를 호출한 후 옵저버블은 스트림 아래로 값을 푸시하지 않습니다.

 import { Observable } from 'rxjs' const observable$ = new Observable(function subscribe(subscriber) { const intervalId = setInterval(() => { subscriber.next('hi'); subscriber.complete() clearInterval(intervalId); }, 1000); }); observable$.subscribe( value => console.log(`Value is ${value}`), err => console.log(err) )

위의 예는 1000밀리초 후에 Value is hi 를 인쇄합니다.

매번 수동으로 관찰 가능 항목을 만드는 것은 장황하고 지루할 수 있습니다. 따라서 RxJS에는 Observable을 생성하는 많은 기능이 있습니다. 가장 일반적으로 사용되는 것들 중 일부는 of , fromajax 입니다.

of 는 일련의 값을 가져와 스트림으로 변환합니다.

 import { of } from 'rxjs' of(1, 2, 3, 'Hello', 'World').subscribe(value => console.log(value)) // 1 2 3 Hello World

~에서

from 거의 모든 것을 값의 스트림으로 변환합니다.

 import { from } from 'rxjs' from([1, 2, 3]).subscribe(console.log) // 1 2 3 from(new Promise.resolve('Hello World')).subscribe(console.log) // 'Hello World' from(fibonacciGenerator).subscribe(console.log) // 1 1 2 3 5 8 13 21 ...

아약스

ajax 는 문자열 URL을 사용하거나 HTTP 요청을 만드는 옵저버블을 생성합니다. ajax 에는 ajax.getJSON 함수가 있어 ajax() 가 반환하는 다른 속성 없이 AJAX 호출에서 중첩된 응답 객체만 반환합니다.

 import { ajax } from 'rxjs/ajax' ajax('https://jsonplaceholder.typicode.com/todos/1').subscribe(console.log) // {request, response: {userId, id, title, completed}, responseType, status} ajax.getJSON('https://jsonplaceholder.typicode.com/todos/1').subscribe(console.log) // {userId, id, title, completed} ajax({ url, method, headers, body }).subscribe(console.log) // {...}

Observable을 만드는 더 많은 방법이 있습니다(여기에서 전체 목록을 볼 수 있습니다).

연산자

연산자는 필요한 거의 모든 것에 대한 연산자가 있는 RxJS의 진정한 강자입니다. RxJS 6부터 연산자는 옵저버블 객체의 메소드가 아니라 .pipe 메소드를 사용하여 옵저버블에 적용된 순수 함수입니다.

지도

map 은 단일 인수 함수를 사용하고 스트림의 각 요소에 투영을 적용합니다.

 import { of } from 'rxjs' import { map } from 'rxjs/operators' of(1, 2, 3, 4, 5).pipe( map(i=> i * 2) ).subscribe(console.log) // 2, 4, 6, 8, 10 

지도

필터

filter 는 단일 인수를 취하고 주어진 함수에 대해 false를 반환하는 값을 스트림에서 제거합니다.

 import { of } from 'rxjs' import { map, filter } from 'rxjs/operators' of(1, 2, 3, 4, 5).pipe( map(i => i * i), filter(i => i % 2 === 0) ).subscribe(console.log) // 4, 16 

필터

플랫맵

flatMap 연산자는 Steam의 모든 항목을 다른 스트림으로 매핑하고 이러한 스트림의 모든 값을 평면화하는 함수를 사용합니다.

 import { of } from 'rxjs' import { ajax } from 'rxjs/ajax' import { flatMap } from 'rxjs/operators' of(1, 2, 3).pipe( flatMap(page => ajax.toJSON(`https://example.com/blog?size=2&page=${page}`)), ).subscribe(console.log) // [ { blog 1 }, { blog 2 }, { blog 3 }, { blog 4 }, { blog 5 }, { blog 6 } ] 

플랫맵

병합

merge 는 두 스트림의 항목을 도착한 순서대로 병합합니다.

 import { interval, merge } from 'rxjs' import { pipe, take, mapTo } from 'rxjs/operators' merge( interval(150).pipe(take(5), mapTo('A')), interval(250).pipe(take(5), mapTo('B')) ).subscribe(console.log) // ABAABAABBB 

병합

운영자의 전체 목록은 여기에서 확인할 수 있습니다.

Redux 관찰 가능

Redux 관찰 가능

기본적으로 Redux의 모든 작업은 동기식입니다. Redux-observable은 관찰 가능한 스트림을 사용하여 비동기 작업을 수행한 다음 해당 비동기 작업의 결과로 Redux에서 다른 작업을 전달하는 Redux용 미들웨어입니다.

Redux-observable은 Epic 개념을 기반으로 합니다. 에픽은 일련의 행동을 취하고 선택적으로 상태의 흐름을 취하고 행동의 흐름을 반환하는 함수입니다.

함수(action$: 관찰 가능 , state$: StateObservable ): 관찰 가능 ;

관례에 따라 스트림(_aka _observable )인 모든 변수는 $ 로 끝납니다. redux-observable을 사용하려면 먼저 스토어에 미들웨어로 추가해야 합니다. 에픽은 관찰 가능한 스트림이고 이 스팀에서 나오는 모든 작업은 스트림으로 다시 파이프되기 때문에 동일한 작업을 반환하면 무한 루프가 발생합니다.

 const epic = action$ => action$.pipe( filter(action => action.type === 'FOO'), mapTo({ type: 'BAR' }) // not changing the type of action returned // will also result in an infinite loop ) // or import { ofType } from 'redux-observable' const epic = action$ => action$.pipe( ofType('FOO'), mapTo({ type: BAZ' }) )

이 리액티브 아키텍처를 모든 파이프의 출력이 자체를 포함한 모든 파이프와 Redux의 리듀서로 피드백되는 파이프 시스템으로 생각하십시오. 무엇이 들어가고 무엇이 막히는지를 결정하는 것은 이 파이프 위에 있는 필터입니다.

탁구 서사시가 어떻게 작동하는지 봅시다. 핑을 받아 서버로 보내고 요청이 완료된 후 퐁을 앱으로 다시 보냅니다.

 const pingEpic = action$ => action$.pipe( ofType('PING'), flatMap(action => ajax('https://example.com/pinger')), mapTo({ type: 'PONG' }) ) Now, we are going to update our original todo store by adding epics and retrieving users. import { combineReducers, createStore } from 'redux' import { ofType, combineEpics, createEpicMiddleware } from 'redux-observable'; import { map, flatMap } from 'rxjs/operators' import { ajax } from 'rxjs/ajax' // ... /* user and todos reducers defined as above */ const rootReducer = combineReducers({ user, todos }) const epicMiddleware = createEpicMiddleware(); const userEpic = action$ => action$.pipe( ofType('GET_USER'), flatMap(() => ajax.getJSON('https://foo.bar.com/get-user')), map(user => ({ type: 'GET_USER_SUCCESS', payload: user })) ) const addTodoEpic = action$ => action$.pipe( ofType('ADD_TODO'), flatMap(action => ajax({ url: 'https://foo.bar.com/add-todo', method: 'POST', body: { text: action.payload } })), map(data => data.response), map(todo => ({ type: 'ADD_TODO_SUCCESS', payload: todo })) ) const completeTodoEpic = action$ => action$.pipe( ofType('COMPLETE_TODO'), flatMap(action => ajax({ url: 'https://foo.bar.com/complete-todo', method: 'POST', body: { id: action.payload } })), map(data => data.response), map(todo => ({ type: 'COMPLEE_TODO_SUCCESS', payload: todo })) ) const rootEpic = combineEpics(userEpic, addTodoEpic, completeTodoEpic) const store = createStore(rootReducer, applyMiddleware(epicMiddleware)) epicMiddleware.run(rootEpic);

_중요: Epic은 RxJS의 다른 관찰 가능한 스트림과 같습니다. 완료 또는 오류 상태가 될 수 있습니다. 이 상태가 지나면 에픽과 앱이 작동을 멈춥니다. 따라서 스팀의 모든 잠재적 오류를 잡아야 합니다. 이를 위해 __catchError__ 연산자를 사용할 수 있습니다. 추가 정보: redux-observable의 오류 처리 .

반응형 Todo 앱

일부 UI가 추가되면 (최소) 데모 앱은 다음과 같이 보입니다.

반응형 Todo 앱
이 앱의 소스 코드는 Github에서 사용할 수 있습니다. Expo에서 프로젝트를 시도하거나 Expo 앱에서 위의 QR 코드를 스캔하십시오.

React, Redux 및 RxJS 튜토리얼 요약

반응형 앱이 무엇인지 배웠습니다. 또한 Redux, RxJS 및 redux-observable에 대해 배웠고 Expo에서 React Native를 사용하여 반응형 Todo 앱도 만들었습니다. React 및 React Native 개발자의 경우 현재 추세는 몇 가지 매우 강력한 상태 관리 옵션을 제공합니다.

다시 한 번, 이 앱의 소스 코드는 GitHub에 있습니다. 아래 댓글에서 반응형 앱의 상태 관리에 대한 생각을 자유롭게 공유하세요.