RxJS with React: 액션 및 테마

RxJS와 React는 초콜릿과 땅콩잼처럼 결합되어 있다. 각자 훌륭하지만 결합되면 신기해진다.

행동


에서 React 내장 갈고리 useStateuseEffect 구독 RxJS 관찰 대상을 어떻게 사용하는지 연구했습니다.사용자 정의 useObservable 갈고리를 만들었습니다. RxJS Observable를 전달하면 Observable의 현재 값을 되돌려주고 값이 변경될 때마다 다시 렌더링합니다.
React 커뮤니티에서 과거에는 데이터 하락과 동작 상승(DDAU)의 개념에 대해 더 많이 토론했다.우리의 useObservable 맞춤형 갈고리를 사용하면 우리는 이미'데이터 아래로'부분을 포장했기 때문에 지금 우리는 이러한 조작을 처리해야 한다.
행동은 도대체 무엇입니까?
오늘날 우리가 말하는 반응 행위는 두 가지 의미가 있다.
  • 최초의 뜻은 함수(리셋)를 부모 구성 요소와 통신하는 도구로 전달하는 것이다.React에는 양방향 연결이 없습니다. 따라서 하위 구성 요소가 부모 구성 요소에 단추를 누르거나 입력을 변경했다고 알려주려면 도구로 전달되는 함수를 호출합니다. 이를 위탁 모드라고 합니다.동작은 단지 위탁 속성 함수(리셋)를 호출하는 행위일 뿐, 예를 들어 onChange 또는 onPause.
  • 그러나 Redux는 행동의 새로운 의미를 보급시켰다.Redux에서 동작은 사용자의 상호작용이나 의도를 서열화할 수 있는 대상입니다.일반적으로, 그것들은 type 속성과 선택 사항payloadmeta 속성을 가지고 있다.동작 대상은 저장된 dispatch() 방법을 호출하여 동작 대상을 매개 변수로 Redux 저장소에 전달합니다.
  • 그러나 동작이 위탁 리셋 함수를 호출하는 행위든 dispatch 호출에 전달된 의도를 설명하는 대상이든 상관없이'무슨 일이 일어났는지'라는 생각을 대표한다. 나는 부모 구성 요소, 저장 또는 어떤 것에 메시지를 보내서 무슨 일이 일어났는지 설명하고 싶다.

    사회과학


    RxJSSubjects는 스케줄링 작업에 적합합니다.Subjects는 특수한 관찰 대상이자 관찰자로서도 사용할 수 있다. 왜냐하면 그들은 observer 인터페이스를 실현했기 때문이다. 이것은 단지 화려한 표현일 뿐이고 next(), error(), complete() 방법을 가지고 있기 때문이다.테마의 .next() 방법을 호출하여 동작을 스케줄링하고 필요한 이벤트에 대한 설명을 매개 변수로 전달할 수 있습니다. 저희가 .next() 전달하는 매개 변수는 테마의 모든 관찰자에게 발송됩니다. 때로는 구독자라고도 합니다.
    우리는 RxJSSubject를 사용하여 Redux 상점 같은 것을 실현할 수 있다.우리는 현재 상태와 우리가 동작State을 사용하여 얻은 동작의 관찰 가능한 값으로 우리의 Subject 관찰 가능한 값을 내보낼 수 있다.
    이것이 무엇을 의미하는지 더 잘 알기 위해서, 우리가 작성한 간단한 useObservable 사용자 정의 갈고리를 사용하여 간단한count 작은 위젯을 만듭니다.
    상태의 관찰 가능 값 (count) 을 만들고 동작의 관찰 가능 값 + 현재 상태를 제거합니다.
    // this will be an observable of `increment` or `decrement` strings
    const action$ = new Subject();
    // map the action strings to a state update number
    const update$ = action$.pipe(
      map((action) => (action === "increment" ? +1 : -1))
    );
    // update the state by summing the state and the update
    const count$ = update$.pipe(
      startWith(0), // our initial state will be 0
      scan((count, update) => count + update)
    );
    
    ...count$ 갈고리를 사용하여 useObservable 상태의 작은 위젯 구성 요소를 관찰하고 사용자 정의할 수 있으며, count 테마를 사용하여 action$ 또는 increment 동작을 decrement 방법으로 상태를 업데이트할 수 있습니다.
    const CountWidget = () => {
      const count = useObservable(count$);
      return (
        <div className="count-widget">
          <button onClick={() => action$.next("decrement")}>-</button>
          <span>{count}</span>
          <button onClick={() => action$.next("increment")}>+</button>
        </div>
      );
    };
    
    다음은 상술한 내용의 간단한 시범이다.
    이것은 너무 간단하지만, 이 생각은 더욱 유용한 것으로 확장될 수 있다.만약 우리가 같은 기술을 사용자 이름 취득 프로그램과 결합한다면, 우리는 사용자가 프로젝트 목록에서 내비게이션을 할 수 있도록 페이지 나누기 기능을 쉽게 추가할 수 있다.구성 요소에서 스케줄링된 action$.next()'back' 작업의 관찰성을 수용하고 이를 바탕으로 API 호출 중인 'forward' 검색 매개 변수를 증가하거나 감소함으로써 사용자의 새로운'페이지'를 얻을 수 있는 리셋 함수를 만들었습니다.
    이 예는 좀 복잡하지만 생각은 같다. 동작에서 파생된'페이지 번호'의 관찰 가능한 값을 만들고 page 관찰 가능한 값을 사용하여 API에서 파생 이름 목록을 호출한다.

    유사한 페이지 내용 $


    React 내장 갈고리 useReducer 의 장점 중 하나는 구성 요소 외부에서 감속기를 정의할 수 있다는 것이다.Reducer 함수를 독립적으로 테스트할 수 있으며, useReducer 에 전달할 때 React는 상태만 업데이트하고 구성 요소를 자동으로 다시 렌더링합니다.useReducer의 갈고리를 같은 품질로 바꾸자.
    이 점을 실현하기 위해서 우리는 함수를 얻기 위해 갈고리를 바꿀 것이다.useObservable에 전달된 함수는 관찰할 수 있는 동작 (구성 요소에서 보내는 동작) 을 매개 변수로 수신하고 새로운 상태의 관찰할 수 있는 값을 되돌려줍니다.우리는 useObservable 이후에 사용자 정의 갈고리를 위한 API를 모델링할 것이기 때문에 하나의useObservable .
    이런 방식을 통해 우리는 개발자들이 이미 스케줄링된 작업에 어떻게 응답하고 상태에 어떻게 영향을 미치는지 결정할 수 있다.
    다음과 같이 하십시오.
    useObservable((action$) => {
      // let the developer decide how the action$ Observable affects the state
      actions$.pipe(/* … */);
      // returns an observable that emits the new state
      return newState$;
    });
    
    따라서 우리의 새로운 useReducer() 맞춤형 연결을 실현하기 위해 우리는 다음과 같이 할 것이다.
  • 리셋 함수[state, dispatch]를 매개 변수로 한다.
  • RxJSuseObservable()를 생성하여 우리fn로 관찰할 수 있습니다.
  • 함수를 만들고 그 매개 변수를 Subject에 전달합니다.
  • 호출action$을 통해 리셋하고 dispatch 매개 변수로 전달
  • , 관찰할 수 있는 action.next() 만들기
  • 이전
  • 과 동일한state$/fn 기술을 사용하여 action$ 밀어내기state
  • state$useState 함수를 useEffect 메타그룹
  • 으로 반환
    이렇게 하면 우리는 이런 결과를 얻을 수 있다.
    const useObservable = (callback) => {
      // create the action$ observable only 1 time
      const action$ = useRef(new Subject()).current;
      // the dipatch function is memoized with useCallback()
      const dispatch = useCallback((v) => action$.next(v), [action$]);
      // store the callback on a ref, ignoring any new callback values
      const fn = useRef(callback).current;
    
      const [state, setState] = useState();
      useEffect(() => {
        // use the callback to create the new state$ observable
        const state$ = fn(action$);
    
        const sub = state$.subscribe(setState);
        return () => sub.unsubscribe();
      }, [fn, action$]);
    
      return [state, dispatch];
    };
    
    이것은 약간 state 처럼 보인다. dispatch 상태에 대한 동기화 업데이트만 제한하는 것을 제외하고, 우리의 [state, dispatch] 는 시간에 따라 상태를 업데이트할 수 있다.또한 저희 useReducer 는 안전한 비동기 갈고리입니다. 정리할 때 구독을 취소하기 때문에 마운트 해제 후 구성 요소 상태를 업데이트하는 것을 걱정할 필요가 없습니다.

    업데이트 예


    이제 이 함수가 있으면 useReducer 함수를 정의할 수 있습니다. 이 함수는 예상된 useObservable 인터페이스를 따릅니다.우리의 useObservable 기능은 우리의 구성 요소와 분리할 수 있다.우리는 이론적으로 서로 다른 구성 요소에서 같은 기능을 사용하는 것을 독립적으로 테스트할 수 있다.우리는 이름 추출 기능을 자신의 파일에 추출하고 이 함수 getUserNames() 를 내보낼 것입니다.
    import { map, startWith, scan, switchMap } from "rxjs/operators";
    import { ajax } from "rxjs/ajax";
    
    const api = `https://randomuser.me/api/?results=5&seed=rx-react&nat=us&inc=name&noinfo`;
    const getName = (user) => `${user.name.first} ${user.name.last}`;
    
    export const getUserNames = (action$) => {
      const actionMap = {
        forward: +1,
        back: -1,
      };
    
      const page$ = action$.pipe(
        scan((page, action) => page + actionMap[action], 1),
        startWith(1)
      );
    
      return page$.pipe(
        switchMap((page) => ajax.getJSON(`${api}&page=${page}`)),
        map(({ results }) => results.map(getName))
      );
    };
    
    그러면 다음과 같이 구성 요소useObservable와 새 구성 요소getUserNames()를 가져옵니다.
    function App() {
      const [names, dispatch] = useObservable(getUserNames);
    
      return (
        <div className="App">
          <h1>RxJS with React</h1>
          <List items={names} />
          <button onClick={() => dispatch("back")}></button>
          <button onClick={() => dispatch("forward")}></button>
        </div>
      );
    }
    
    다음은 전체 예제입니다.
    나는 이것이 매우 좋은 모델이라고 생각한다. 구성 요소의 작용은 뚜렷하고 실제 데이터를 검색하는 방식과 연결된다는 것을 나타낸다. 이것은 통량 모델을 따르고 보통 구성 요소의 상태와 부작용의React모델과 잘 일치한다.

    비록 이것은 표면적인 현상일 뿐이지만 우리의 getUserNames 갈고리는 현재 상태를 리셋 함수에 노출하고 기억과 다른 기술을 사용하여 성능을 향상시키며 구성 요소 도구/상태를 도구로 리셋 함수에 노출시키는 방법을 제공하는 등 여러 방면에서 개선될 수 있다.
    이러한 생각의 강력한 실현을 원한다면 GitHub에서 내 라이브러리use-epic를 볼 수 있습니다. 이 라이브러리는 매우 유사한 패턴을 따릅니다.

    비가포 / epic 사용


    RxJS Epics를 React 구성 요소로 사용하여 상태 관리


    React와 RxJS를 혼합하여 사용할 때 애니메이션, 실시간 업데이트, 실체 저장...명세서에 아직 많아요.만약 당신이 이 화제에 흥미가 있다면, 평론에서 저에게 알려주세요.

    좋은 웹페이지 즐겨찾기