Skip to content

Commit

Permalink
🔨 Allow any element to be store provider
Browse files Browse the repository at this point in the history
  • Loading branch information
NeoLegends committed Feb 26, 2018
1 parent 0e42f44 commit 35e2284
Show file tree
Hide file tree
Showing 4 changed files with 38 additions and 50 deletions.
19 changes: 7 additions & 12 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ fit-html is a combination of [lit-html](https://github.com/Polymer/lit-html), we

You need the following:
```js
import { connect, createProvider } from 'fit-html';
import { connect, withStore } from 'fit-html';
import { html } from 'lit-html/lib/lit-extended';
import { createStore } from 'redux';
```
Expand All @@ -33,12 +33,6 @@ const todos = (state = [], action) => {
const store = createStore(todos, ['Use Redux']);
```

Set up redux provider element (this must be at the root of your element tree):
```js
const provider = createProvider(store);
customElements.define('redux-provider', provider);
```

Define actions and view:
```js
function addTodo() {
Expand All @@ -58,11 +52,14 @@ const render = ({ addTodo, todos }) => html`
</button>
`;

const TodosApp = connect(
// The withStore mixin is only required for the root element of your
// app. All other 💪-elements will get the redux store from that element.

const TodosApp = withStore(connect(
state => ({ todos: state }),
{ addTodo },
render
);
), store);

customElements.define('todo-app', TodosApp);
```
Expand All @@ -74,9 +71,7 @@ customElements.define('todo-app', TodosApp);
<title>My cool 💪-html app</title>
</head>
<body>
<redux-provider>
<todo-app></todo-app>
</redux-provider>
<todo-app></todo-app>
</body>
</html>
```
Expand Down
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "fit-html",
"version": "0.5.2",
"version": "0.5.3",
"description": "5KB functional Web Components without bloat",
"main": "dist/index.js",
"types": "dist/index.d.ts",
Expand Down
14 changes: 4 additions & 10 deletions src/connect.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,6 @@ import { render as shadyRender } from 'lit-html/lib/shady-render';
import { bindActionCreators, ActionCreatorsMapObject, Dispatch, Store, Unsubscribe } from 'redux';

import { ClassConstructor } from '.';
import { ProviderElement } from './provider';

/**
* The function that extracts required data out of the state and the passed props.
Expand Down Expand Up @@ -154,14 +153,13 @@ export default function connect<S, SP, DP, OP = {}>(

let node: any = this;
while (node = node.parentNode || node.host) {
const maybeStore = node._store || node.reduxStore;
if (isReduxStore<S>(maybeStore)) {
this._store = maybeStore;
return maybeStore;
if (isFunction(node.getStore)) {
this._store = node.getStore();
return this._store;
}
}

throw new Error("💪-html: Missing redux store.\nSeems like you're using fit-html without a redux store. Please use the provider component to provide one to the element tree.");
throw new Error("💪-html: Missing redux store.\nSeems like you're using fit-html without a redux store. Please use a provider component to provide one to the element tree.");
}

getProps(ownProps = {} as OP): SP & DP {
Expand Down Expand Up @@ -205,10 +203,6 @@ function isFunction(f: any): f is Function {
return typeof f === 'function';
}

function isReduxStore<S>(obj: any): obj is Store<S> {
return obj && obj.getState && obj.dispatch && obj.subscribe;
}

function shallowEqual(a, b) {
if (a === b) {
return true;
Expand Down
53 changes: 26 additions & 27 deletions src/provider.ts
Original file line number Diff line number Diff line change
@@ -1,17 +1,17 @@
import { Store } from 'redux';

import { ClassConstructor } from '.';
import { ClassConstructor, FitElement } from '.';

/**
* A 💪 redux store provider element.
* Creates a subclass of the given HTML element that supplies the redux store to its DOM children.
*
* This element supplies the redux store to the 💪-html elements below it.
* Thus, all other 💪-elements must be a child of that element.
* It works much like {@see https://github.com/reactjs/react-redux/blob/master/docs/api.md#provider-store}.
*
* When connected to the document, 💪-elements walk the dom upwards until they
* either find another 💪-element that already has an initialized redux store
* or the store provider element. They then take the store from that given
* element, subscribe to it for rendering and cache it for further lookups
* element, subscribe to the store for rendering and cache it for further lookups
* or potential 💪-children.
*
* You usually only need one per application as you would usually only use one
Expand All @@ -23,40 +23,39 @@ import { ClassConstructor } from '.';
*
* @example
* ```html
* <redux-provider-top-level>
* <!-- These two elements use the store from redux-provider-top-level -->
* <app-shell>
* <!-- These two elements use the store from app-shell -->
* <fit-element-1></fit-element-1>
* <fit-element-2></fit-element-2>
*
* <redux-provider-sub-level>
* <!-- These two elements use the store from redux-provider-sub-level -->
* <fit-element-2></fit-element-1>
* <sub-shell>
* <!-- These two elements use the store from sub-shell -->
* <fit-element-1></fit-element-1>
* <fit-element-2></fit-element-2>
* </redux-provider-sub-level>
* </redux-provider-top-level>
* </sub-shell>
* </app-shell>
* ```
*/
export declare class ProviderElement<S> extends HTMLElement {
/**
* The previously created redux store to be accessible for all children.
*/
reduxStore: Store<S>;
}

/**
* Creates a new redux store provider element using the given store.
*
* This element supplies the redux store to the 💪-html elements below it.
* Thus, all 💪-elements must be a child of this element.
*
* @param {T} clazz The base class to extend from.
* @param {Store<S>} store The redux store.
* @returns {ProviderElement<S>} The redux store provider element class.
* @template S
*/
export default function createProvider<S>(store: Store<S>): ClassConstructor<ProviderElement<S>> {
return class extends HTMLElement {
get reduxStore(): Store<S> {
export function withStore<T extends ClassConstructor<HTMLElement>, S>(clazz: T, store: Store<S>) {
return class extends clazz {
getStore(): Store<S> {
return store;
}
};
}

/**
* Creates a new HTML element that supplies the redux store to its DOM children.
*
* Thus, all other 💪-elements must be a child of that element.
*
* @param {Store<S>} store The redux store.
* @deprecated Use `withStore` on your app shell / root element instead.
*/
export const createProvider = <S>(store: Store<S>) => withStore(HTMLElement, store);
export default createProvider;

0 comments on commit 35e2284

Please sign in to comment.