0.4.8 • Published 3 years ago

@raviupadhyay/reactrx v0.4.8

Weekly downloads
-
License
-
Repository
-
Last release
3 years ago

JSX based rendering library

This is a jsx based reactive rendering library. Since nowadays react and similar virtual dom libraries are quite famous, I will try to introduce this with the context of react. The major difference from react is that it does not have virtual dom. Also, unlike svelte/solid, there is no compilation step involved, other than the jsx compilation to functions.

import React, {render} from 'noreact';
import {BehaviorSubject, Subject, Observable} from 'rxjs';
import {tap, takeWhile, scan, map} from 'rxjs/operators';

The function timer will be called just once. In react a function is called again as soon as it's state, or props change. Instead, we just call the function once, and it returns a normal dom node. Here div node has a reactive child, x, which is Observable class from rxjs library. The rendering library detects that x is reactive, so it listens to it, and calls replaceWith method of the child text node to replace with new element.

function Timer({clean}) {
  const x = interval(1000).pipe(takeUntil(clean));
  return <div>{x}</div>;
}

Library is super small (excluding rxjs part). And rxjs is a library which should be present in any UI application. The library is just ~75 lines of code which includes propers spaces and indentation.

Let's check the typical Form component. Instead of state, we can use a Subject (Subject is an observable which can be subscribed multiple times). Notice how the subject x looks similar to a typical store with actions.

To be as close to HTML conventions as possible, the event handlers are fully untouched. If an attribute is function for example update is a function, so the dom node will register it is node.oninput=update, otherwise node.setAttribute(key, value) is used. Thus, there is no need to learn any other event handling.

function Form() {
  const x = new BehaviorSubject({}).pipe(
    scan((obj, curr) => {
      return {
        ...obj,
        ...curr
      };        
    }, {
      firstname: '',
      lastname : '',
    })
  );
  const firstname = x.pipe(map(obj => obj.firstname));
  const lastname = x.pipe(map(obj => obj.lastname));
  function update(e) {
    x.next({[e.target.name]: e.target.value});
  }
  return (
    <div onclick={onclick}>
      <input name="firstname" oninput={update} />
      <br />
      <input name="lastname"  oninput={update} />
      <br />
      Hi {lastname}, {firstname}
    </div>
  );
}

Cleanup logic is handled by defer function is is passed as prop. Note that, in Switch component, each time, switch button is clicked, the comp is updated with a new component instance. So if you switch back to timer component, it will restart from 0. Also, whenever there is update on {comp}, everything that was deferred for cleanup will be cleaned up for that older component. Whenever a functional component is called, two pair of functions are created defer and clean. defer is passed to the function, which can register the resources which needs to be cleaned up. And clean function is attached to the resultant dom node.

function Switch() {
  let bool = true;
  let comp = new BehaviorSubject(<Timer />);
  function onclick() {
    bool = !bool;
    if (bool === true) {
      comp.next(<Form />);
    } else {
      comp.next(<Timer />);
    }
  }
  return (
    <div>
      <button onclick={onclick}>Switch</button>
      {comp}
    </div>
  );
}

SwitchWithState component is similar to the above Switch component. But there is a difference. When switch button is clicked, and the comp is updated, the state of the component which is being unmounted is preserved, and in turn the component which is being mounted again is also in working state. This happens because when we are creating Timer component, we are passing defer function ourselves, which means that the resources which are deferred in the Timer component will be cleaned only when it's corresponding clean function will be called. And the corresponding clean function will be called only when SwitchWithState is replaced with another node in dom.

function SwitchWithState({defer}) {
  const a = <Form  defer={defer}/>;
  const b = <Timer defer={defer}/>;
  const comp = new BehaviorSubject(true).pipe(
    scan((acc, curr) => acc == a ? b : a, a)
  );
  function onclick() {    
    comp.next(true);
  }
  return (
    <div>
      <button onclick={() => comp.next(true)}>Switch</button>
      {comp}
    </div>
  );
}


render(<SwitchWithState />, document.getElementById('root'));
0.4.8

3 years ago

0.3.0

3 years ago

0.4.5

3 years ago

0.2.7

3 years ago

0.4.4

3 years ago

0.2.6

3 years ago

0.4.7

3 years ago

0.4.6

3 years ago

0.4.1

3 years ago

0.4.0

3 years ago

0.3.1

3 years ago

0.4.3

3 years ago

0.4.2

3 years ago

0.2.1

3 years ago

0.2.3

3 years ago

0.2.2

3 years ago

0.2.5

3 years ago

0.2.4

3 years ago

0.2.0

4 years ago

0.1.0

4 years ago