Git Product home page Git Product logo

lit-redux-watch's Introduction

lit-redux-watch

Build Status - Travis CI Test Coverage - Code Climate GPL-3.0 NPM

Attach Redux store state and Reselect selectors to properties in LitElement with the @watch() decorator or a static watch getter.

Install

npm i lit-redux-watch

API documentation

We generate API documentation with TypeDoc.

API Documentation

Usage

Introduction examples

With the @watch decorator

This example uses TypeScript with decorators to register properties with watchers. Decorators are an experimental feature but can be used by enabling them in your tsconfig.json. More information can be found in the TypeScript Documentation.

The TC39 proposed decorators which are currently in Stage 2 will be supported in the future.

import {LitElement, html} from 'lit-element';
import {connect, watch} from 'lit-redux-watch';
import {store} from './store'; // Your redux store

class UserPage extends connect(store)(LitElement) {

    @watch('user.name')
    userName?: string;
    
    render() {
      return html`
        <div>Name: ${this.userName}</div>
      `;
    }

}

With a static watch getter

import {LitElement, html} from 'lit-element';
import {connect} from 'lit-redux-watch';
import {store} from './store'; // Your redux store

class UserPage extends connect(store)(LitElement) {

    static get watch() {
        return {
            userName: 'user.name'
        }
    }

    render() {
      return html`
        <div>Name: ${this.userName}</div>
      `;
    }

}

Setup

connect([store[, options]])

To use lit-redux-watch in a class, use the connect mixin.

class MyElement extends connect()(LitElement) {}

class MyElement extends connect(store)(LitElement) {}

class MyElement extends connect(store, options)(LitElement) {}

When no store is provided via connect one must be provided via static get watch/@watch.

@watch(source[, options][, store])

To create a watching property, use the @watch decorator.

@watch('user.firstName')
userFirstName?: string;

@watch('user.lastName', options)
userLastName?: string;

@watch('user.email', store)
userEmail?: string;

@watch('user.address', options, store)
userAddress?: string;

// Or a function
@watch((state) => state.user.phone || state.user.mobile)
userPhoneNumber?: string;

// Or a reselect selector
@watch(userLanguagesSelector)
userLanguages?: string[];

See the sections on sources and options for more information about what sources and options can be used.

static get watch() {}

To create a watching property, use the static watch getter.

static get watch() {
    return {
        userFirstName: {
            source: 'user.firstName',
            store: store,

            // Options are added in the same object
            noInit: true,
            transform: (next) => next.trim(),
        }
    }
}
NOTE

Options and stores provided via static get watch/@watch override those provided in the connect mixin.

Source

Path-string source

A simple path-string can be used as a source.

@watch('shop.items')
shopItems?: ShopItem[];

Path-strings are like object paths, so the above maps to:

store.getState().shop.items

The difference is that if the string-path can not be traversed all the way undefined is returned instead of an error being thrown.

Function source

Instead of a path-string a function can also be used as source.

@watch((state) => state.user.firstName)
userFirstName?: string;

The function gets called with the store state as the first parameter and should return the value for lit-redux-watch to use.

Reselect selector source

Reselect selectors can also be used as a source since they return a function compatible with the pattern mentioned in Function source.

Simply create a selector...

const shopItemsSelector = (state) => state.shop.items;
const subtotalSelector = createSelector(
    shopItemsSelector,
    items => items.reduce((acc, item) => acc + item.value, 0),
);

...and use it as your watch source.

@watch(subtotalSelector)
subtotal?: number;

For more information about Reselect see the reselect documentation.

Options

These are the available options for watchers.

{
    /**
     * Override the default strict === compare function. If you need deep
     * equal you can use epoberezkin/fast-deep-equal.
     */
    compare: function(a, b) {
        return a === b;
    },

    /**
     * Indicates whether the value should be loaded from redux on init. By
     * default the value will be loaded when the property is initialized
     * but this can be disabled by setting `noInit: true`.
     */
    noInit: false,

    /**
     * A function that is called when a new value is found that should
     * return a boolean to indicate whether the value should be updated.
     */
    shouldUpdate: function(nextValue, oldValue, source) {
        return nextValue !== "";
    },

    /**
     * A function that is called when the value is updated that allows
     * the new value to be transformed. Note: this does not transform the
     * value that is passed as the second argument of the shouldUpdate and
     * transform functions.
     */
    transform: function(nextValue, oldValue, source) {
        return nextValue.trim();
    }
}

Credits

lit-redux-watch is inspired by jprichardson/redux-watch and the property registration from Polymer/lit-element

Coming soon

  • The TC39 proposed decorators which are currently in Stage 2 will be supported in the future.
  • Maybe dispatching actions back to redux on property set will be added. Currently looking into this.

License

GPL-3.0

Made by Paul Gerarts

lit-redux-watch's People

Contributors

gerarts avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar

lit-redux-watch's Issues

LitElement changed lifecycle hook doesn't fire when Redux store changes

When using the @watch decorator on the Redux store, if another component modifies the store, I would expect the changed lifecycle hook to fire in the component, but it currently does not.

export class MyElement extends connect(store)(LitElement) {
    constructor() {
      super();
    }

    @watch('restaurants.restaurantList')
    public restaurantList: any = {
      data: []
    }

    changed(something: any) {
      // this doesn't fire when `this.restaurantList` changes 
      console.log("something changed: ", something);
    }

Using watch as a normal function

Is it possible to use watch as a normal function and not as a watch decorator?
I am using your package because of course the decorators are best suited but I am facing a situation where I need to update my view from a selector and that selector depends on one variable on that custom Element. Let me provide you with an example

Somewhere :

render () {
  const myCustomElement = new CustomElement('2k32fa0auiccDc2F')
  return html`
    ${myCustomElement}
  `
}

Elsewhere :

class CustomElement extends connect(store)(LitElement) {

  constructor (id: string) {
    super()
    
    const selector = (state: RootState) => state.user.documents.find(d => d.id == id).content
    /** ↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑
     * I now need to register a watch with that selector,
     * so I can update that custom element instance only when the target document's content changes.
     * Using the @watch decorator won't work from the static context because `id` is passed on instanciation.
     * /
  }

  render () { ... }
}

If there is a hack, please let me know Thanks

Error when bundling (`this` has been rewritten to `undefined`)

Hello, I am using rollup to bundle my app and getting a (non-fatal) error:

dist/index.html → dist...
(!) Error when using sourcemap for reporting an error: Can't resolve original location of error.
node_modules/lit-redux-watch/dist/components/connect.js: (1:13)
(!) Error when using sourcemap for reporting an error: Can't resolve original location of error.
node_modules/lit-redux-watch/dist/components/connect.js: (1:21)
(!) `this` has been rewritten to `undefined`
https://rollupjs.org/guide/en/#error-this-is-undefined
node_modules/lit-redux-watch/dist/components/connect.js
1: var __rest = this && this.__rest || function (s, e) {
                ^
2:   var t = {};
...and 1 other occurrence
node_modules/marked/lib/marked.js
1386:   root.marked = marked;
1387: }
1388: })(this || (typeof window !== 'undefined' ? window : global));
         ^
created dist in 13.7s

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.