WebReflection / hyperHTML-Element

An extensible class to define hyperHTML based Custom Elements.
ISC License
202 stars 25 forks source link

Redux integration #12

Closed moebiusmania closed 6 years ago

moebiusmania commented 7 years ago

are you planning to add some Redux integration to the HyperHTML Element like polymer-redux? I know I can write a subscribe listener to the store and update properties on the custom element but I was curious to know if you were already thinking to add e more elegant binding between the two.

WebReflection commented 6 years ago

see, that repo you linked is not from Polymer. I don't have any plan to integrate Redux for the time being but everyone is welcome to use hyperHTML and/or this repo to create any library they want, including a Redux based HyperHTMLElement class.

jiayihu commented 5 years ago

For feedback and future readers, I've based my implementation on pwa-helpers which is more minimal than polymer-redux

import { Store, Unsubscribe, Action } from 'redux';
import { store } from './store';
import HyperHTMLElement from 'hyperhtml-element';

type Constructor<T> = new (...args: any[]) => T;

interface CustomElement {
  connectedCallback?(): void;
  disconnectedCallback?(): void;
  readonly isConnected: boolean;
}

export const connect = <S>(store: Store<S>) => <T extends Constructor<CustomElement>>(
  baseElement: T,
) =>
  class extends baseElement {
    _storeUnsubscribe!: Unsubscribe;

    connectedCallback() {
      if (super.connectedCallback) {
        super.connectedCallback();
      }

      this._storeUnsubscribe = store.subscribe(() => this.stateChanged(store.getState()));
      this.stateChanged(store.getState());
    }

    disconnectedCallback() {
      this._storeUnsubscribe();

      if (super.disconnectedCallback) {
        super.disconnectedCallback();
      }
    }

   stateChanged(_state: S) {}

    dispatch(action: Action) {
      store.dispatch(action);
    }
  };

export const ConnectedHyperElement = connect(store)(HyperHTMLElement);

Used as follows:

import { ConnectedHyperElement } from '../../../../lib/connect';
import { RootState } from '../../../../redux/reducers';
import { TFeed } from '../../../../domain/feeds';
import { getFeeds } from '../../../../services/feeds.service';
import { addFeeds } from '../../../../redux/actions/feeds.action';
import { wire } from 'hyperhtml';

export class Homepage extends ConnectedHyperElement {
  feeds: TFeed[] = [];

  static get observedAttributes(): Array<keyof Homepage> {
    return [];
  }

  constructor() {
    super();

    this.attachShadow({ mode: 'open' });
  }

  connectedCallback() {
    super.connectedCallback();
    this.render();
    getFeeds().then(feeds => this.dispatch(addFeeds(feeds)));
  }

  stateChanged(state: RootState) {
    this.feeds = state.feeds;
    this.render();
  }

  attributeChangedCallback() {
    this.render();
  }

  render() {
    return this.html`
        <div>
          ${this.feeds.map(feed => wire(feed)`<mr-feed feed=${feed} />`)}
        </div>
    `;
  }
}

Homepage.define('mr-homepage');