frintjs / frint

Modular JavaScript framework for building scalable and reactive applications
https://frint.js.org/
MIT License
754 stars 37 forks source link

Examples: consider different structure for observed components #272

Open fahad19 opened 7 years ago

fahad19 commented 7 years ago

Currently

Most observed components are written like this:

// components/MyComponent.js
import React from 'react';
import { observe, streamProps } from 'frint-react';

const MyComponent = function (props) {
  return (
    <p>App name: {props.name}</p>
  );
};

return observe(function (app) {
  return streamProps()
    .set('name', app.getName())
    .get$();
})(MyComponent);

And to use it somewhere else, we can just import and embed it in JSX:

// components/Root.js
import React from 'react';

import MyComponent from './MyComponent';

export default function Root() {
  return (
    <div>
      <MyComponent />
    </div>
  );
};

Proposal

To make unit testing easier and yet keep related functions in the same file, a new structure can be considered:

// components/MyComponent.js
import React from 'react';
import { observe, streamProps } from 'frint-react';

// MyComponent without being observed
export function Base(props) {
  return (
    <p>App name: {props.name}</p>
  );
}

// props Observable
export function getProps$(app) {
  return streamProps()
    .set('name', app.getName())
    .get$();
}

// the observed component
export const MyComponent = observe(getProps$)(Base); // named export

To import and use elsewhere:

// components/Root.js
import React from 'react';

import { MyComponent } from './MyComponent'; // named imports

export default function Root() {
  return (
    <div>
      <MyComponent />
    </div>
  );
};

This also makes unit testing easier for the components:

// components/MyComponent.spec.js
import { expect } from 'chai';

import { getProps$ } from './MyComponent';

// test the props stream
it('expects to stream app name', function (done) {
  const fakeApp = ''; // mock it

  getProps$(fakeApp)
    .subscribe(function (props) {
      expect(props.name).to.equal('MyAppName');

      done();
    });
});

// test the base component
import { Base } from './MyComponent';
reaktivo commented 7 years ago

We've been doing this as part of the FareKeep implementation, with the only difference being that we do a default export on the observed component.

viacheslaff commented 7 years ago

Good idea. I'm also thinking that making observed component default export is better, just as @reaktivo mentioned. Because then regular code imports the component as usual, while for unit tests there are some additional handles.