RxJS in React - From Class  to Functional - Part 1 Class Components
Tags: react, rxjs, observablehooks, article
Published: 2021-09-25 07:07:29

<sup><sup>Image is from Pixabay user [kordi_vahle](https://pixabay.com/photos/cadini-dolomites-mountains-3573122/)</sup></sup> 

React functional component and hooks pose a slight different model of operation then the standard component lifecycle model.

In order to use [RxJS](https://rxjs.dev/) effectively in React functional components with [hooks](https://reactjs.org/docs/hooks-intro.html), we need to adjust some of the pattern we use in regular components to the new paradigm.

This is [part 1](https://dev.to/elpddev/rxjs-in-react-from-class-to-functional-part-1-class-components-3jgj) of the series. It deals with RxJS patterns for React Class components.

Part 2 will handle React functional components patterns and what is the logic behind them.

![RxJS Logo](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/damw900azmbtrzr80jyx.png)
  
&nbsp; 
  
## RxJS in Regular Class Components 

### Component Lifecycles

In regular components frameworks, there is an `on init` lifecycle that runs once, `on change` lifecycle that runs on each component's input change, and `on destroy` lifecycle that get called when the component is removed and discarded.

The analog to those lifecycles in React are: 
* [componentDidMount](https://reactjs.org/docs/react-component.html#componentdidmount) for `onInit` functionality, 
* [componentDidUpdate](https://reactjs.org/docs/react-component.html#componentdidupdate) for `onChange` functionality, and 
* [componentWillUnmount](https://reactjs.org/docs/react-component.html#componentwillunmount) for `onDestroy` functionality. 

```js
class Calculator {
  constructor(props) {
    // ... 
  }
  
  componentDidMount() {
    // ...
  }

  componentDidUpdate(prevProps, prevState, snapshot) {
    // ... 
  }

  componentWillUnmount() {
    // ...
  }
}
```

&nbsp; 

### Using RxJS with Class Component Lifecycles 


#### Initialization

The `componentDidMount` callback and the `constructor` allow to create the main streams that will guide the component logic and bring them to life - to subscribe to them.

```js
class Calculator {
  constructor(props) {
    super(props);

    /*
     * Initialization of callbacks fucntions 
     * to send stream events
     */
    this.onNumber = (num) => this.numAction$.next(num);
    this.onMathOperation = (operation) => this.mathOperation$.next(operation);

    /*
     * Initialize reflected state of the main streams values.
     * To be used in the view layer.
     */
    this.state = {
      result: "",
      expr: []
    };
  }

  componentDidMount() {
    /* 
     * Setting up the event Rx subjects. Will be used in 
     * the main streams.
     */
    this.onUnmount$ = new Subject();
    this.numAction$ = new Subject();
    this.mathOperation$ = new Subject();

    /* 
     * Setting up the main logic streams.
     */
    this.mathExpression$ = this.streamMathExpression();
    this.result$ = this.streamResult();

    /* 
     * 1. Reflecting the main streams values into
     *    regular state to be used in the view.
     * 2. Subscribing to the main streams to bring
     *    them to life for the duration of the 
     *    component.
     */
    merge(
      this.result$.pipe(
        tap((result) => this.setState((state) => ({ ...state, result })))
      ),
      this.mathExpression$.pipe(
        tap((expr) => this.setState((state) => ({ ...state, expr })))
      )
    )
      .pipe(takeUntil(this.onUnmount$))
      .subscribe();
  }

 /* 
  * Main stream expression
  */
 streamMathExpression() {
    return pipe(
      () =>
        merge(
          this.numAction$.pipe(
            map((num) => ({ action: "number", value: num }))
          ),
          this.mathOperation$.pipe(
            map((op) => ({ action: "operation", value: op }))
          )
        ),
      scan((expr, { action, value }) => {
        // reducer logic for the math expression
      }, [])
    )(null);
  }
}
```

Here, in the  `init` phase of the component, several things happening:

1. Main observables values reflection into React component state.
2. Creating Rx Subjects to be used and notify the main streams of changes and events.
3. Creating and wiring up the main streams that handle the component behavior and state management. 

##### Using Values in the View

This is needed for using the observable values in the view `jsx` layer. In Angular for instance, this is done with the [async](https://angular.io/api/common/AsyncPipe#description) pipe that subscribe and provide the observable value automatically in Angular HTML template expressions.

Initialization of React component state in the constructor: 

```js
constructor() {
  this.state = {
      result: "",
      expr: []
    };
```

Reflecting and updating the main observables values into the React component states. This is done with subscribing to the observables and updating the React component state.

```js
componentDidMount() {
merge(
      this.result$.pipe(
        tap((result) => this.setState((state) => ({ ...state, result })))
      ),
      this.mathExpression$.pipe(
        tap((expr) => this.setState((state) => ({ ...state, expr })))
      )
    )
      .pipe(takeUntil(this.onUnmount$))
      .subscribe();
}
```
##### Creating Rx Subjects to be used and notify the main streams of changes and events

The main streams subscribe to the subjects that are used in the view or in other stream to notify of changes or events such as click and timer events.

```js
constructor(props) {
    super(props);

    this.onNumber = (num) => this.numAction$.next(num);
    this.onMathOperation = (operation) => this.mathOperation$.next(operation);
}
 componentDidMount() {
    this.onUnmount$ = new Subject();
    this.numAction$ = new Subject();
    this.mathOperation$ = new Subject();
}
```

##### Main Streams Logic

The main streams of the component. Those needs to be created and brought to life - subscribed to - only once and live for the rest of the component life until it destroyed.

```js
componentDidMount() {
    this.mathExpression$ = this.streamMathExpression();
    this.result$ = this.streamResult();

    merge(
      this.result$.pipe(
        // ...
      ),
      this.mathExpression$.pipe(
        // ...
      )
    )
      .pipe(takeUntil(this.onUnmount$))
      .subscribe();
}
```

#### Input Change Detection

The `onChange` callback allows to push detected input changes into RxJS subjects. The main streams of the component then can listen on those subject and react to component input changes.

Gathering the changes events:

```js
componentDidMount() {
  this.propsChanges$ = new Subject();
}

componentDidUpdate(prevProps) {
    this.propsChanges$.next({ prev: prevProps, current: this.props });
}
```

Listening and notifying relevant prop changes:

```js
 componentDidMount() {
    this.inputMode$ = handlePropChange("mode", this.propsChanges$);

    merge(
      this.inputMode$,
    )
      .pipe(takeUntil(this.onUnmount$))
      .subscribe();
  }
```

```js
function handlePropChange(propName, propsChanges$) {
  return propsChanges$.pipe(
    filter(({ prev, current }) => prev[propName] !== current[propName]),
    map(({ current }) => current[propName]),
    shareReplay({ bufferSize: 1, refCount: true })
  );
}
```

#### Cleanup

The [componentWillUnmount](https://reactjs.org/docs/react-component.html#componentwillunmount) callback allows to cleanup subscriptions of the main logic streams so no dangling subscriptions will be left after the component is destroyed. 

This is done through another subject - `onUnmount` - that all other main streams subscription depends on so when it emit and complete, the subscriptions of the main streams complete as well. 

The [takeUntil](https://rxjs.dev/api/operators/takeUntil) operator subscribe to the `unMount$` subject and complete the main subscriptions when `unMount` complete.

```js
componentDidMount() {
    this.onUnmount$ = new Subject();
    this.mathExpression$ = this.streamMathExpression();
    this.result$ = this.streamResult();

    merge(
      this.result$.pipe(
        // ...
      ),
      this.mathExpression$.pipe(
        // ...
      )
    )
      .pipe(takeUntil(this.onUnmount$))
      .subscribe();
  }

 componentWillUnmount() {
    this.onUnmount$.next();
    this.onUnmount$.complete();
  }

```

Full example code can be found on [github](https://github.com/elpddev/article-react-rxjs-observable-hooks-class-rxjs);

{% codesandbox gyg9w %}

## Conclusion 

This part dealt with RxJS patterns for React class components. Specifically we saw:

1. How to create main streams for the duration of the component life.
2. Use the streams values in the view layer.
3. Listen on component input props changes.
4. Cleanup subscriptions when the component is destroyed.

Next part will show how to update those patterns to React functional components with hooks and the logic behind those patterns.