Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Support ES6/TypeScript classes in Stores and Action types #444

Closed
szahn opened this issue Jan 7, 2016 · 6 comments
Closed

Support ES6/TypeScript classes in Stores and Action types #444

szahn opened this issue Jan 7, 2016 · 6 comments

Comments

@szahn
Copy link

szahn commented Jan 7, 2016

Be able to use ES6 or TypeScript class as a Reflux Store/Action.

The exception "Uncaught TypeError: Property description must be an object: undefined" is raised in utils.js line 44. The propertyDescriptor variable is null when the prop is a function and that function is in the source prototype instead of the object itself.

The code below is a poor initial attempt to use a TypeScript class as a Reflux store.

var store = class Store {
    listenables = TableActions;
    trigger: Function;

    onAddRow(row: any) {
        console.log("row added!");
        rows.push(row);
        this.trigger(rows);
    }

    onDeleteRow() {
        console.log("row removed!");
    }
};

var s = new store();
var TableStore = Reflux.createStore(s);

Ideally, we would be able to do this in TypeScript. I'm assuming getting this to work with ES6 classes will naturally also work in TypeScript.

class TableStore extends Reflux.Store {
    listenables = TableActions;

    onAddRow(row: any) {
        console.log("row added!");
        rows.push(row);
        this.trigger(rows);
    }

    onDeleteRow() {
        console.log("row removed!");
    }
};
@szahn szahn changed the title Support TypeScript classes in Stores and Action types Support ES6/TypeScript classes in Stores and Action types Jan 8, 2016
@nostalgiaz
Copy link

+1

@vladimir-rovensky
Copy link

+1

@miletbaker
Copy link

If it is of any use in the meantime, I've created a wrapper ES6 class for Reflux.createStore that I'm using to extend from.

'use strict';

import Reflux from 'reflux';

export default class RefluxStore {

  constructor() {
    this._store = Reflux.createStore({});
  }

  listenTo(action, handler) {
    this._store.listenTo(action, handler);
  }

  listenToMany(actions) {
    for (var action in actions) {
      if (actions.hasOwnProperty(action)) {
        let actionName = `${action}`;
        let handlerName = `on${actionName.substring(0,1).toUpperCase()}${actionName.substring(1)}`;
        this._store.listenTo(actions[action], (...args) => {
          let handler = this[handlerName].bind(this);
          if (handler) {
            handler(...args);
          }
        })

      }
    }
  }

  listen(handler) {
    this._store.listen(handler);
  }

  trigger(...args) {
    this._store.trigger(...args);
  }

}

I can then create a store as follows:

'use strict';

import testActions from './testActions';
import RefluxStore from './RefluxStore'

class TestStore extends RefluxStore {

    // Initial setup
    constructor() {
        super();
        // Register testActions
        this.listenToMany(testActions);
    }

    onShowWelcome() {
        console.log(this);
        this.trigger(true);
    }

}

let testStore = new TestStore();

export default testStore;

Note my refluxActions are created normally (I don't see much point wrapping that atm). I'm using this in React Native so I also created a RefluxComponent class I can extend from in my React Native modules instead of Component.

'use strict';

import React, { Component } from 'react-native';

export default class RefluxComponent extends Component {

  constructor(props) {
    super(props);
    this._refluxSubscriptions = [];
  }

  listenTo(store, handler) {
    this._refluxSubscriptions.push(
      store.listen(handler);
    );
  }

  componentWillUnmount() {
    super.componentWillUnmount();
    for (let subscriptionUnsubscribe of this._refluxSubscriptions) {
      subscriptionUnsubscribe();
    }
  }

};

I can then simple create a component as follows:

'use strict';
import React, {
  View,
  Image,
  Text
} from 'react-native';

import RefluxComponent from './RefluxComponent';
import testStore from './testStore';
import testActions from './testActions';

export default class Welcome extends RefluxComponent {

  constructor(props) {
    super(props);
    this.listenTo(testStore, this._handleStore)
    testActions.test();
  }

  _handleStore() {
    console.log("Store Event");
  }

  render() {
    return (
      <View style={this.props.styles.blueContainer}>
        <Image source={require('./assets/logo.png')} />
        <Text style={this.props.styles.welcomeText}>
          Welcome
        </Text>
      </View>
    );
  }

}

This is just thrown together quickly so there is probably a cleverer/more efficient way to do this. It may or may not serve your purpose but I tested it in React Native 0.21 and it is working fine (certainly for my own use cases). Also note I have only wrapped the methods I'm using and Mixins with ES6 are a whole other problem.

@BryanGrezeszak
Copy link
Contributor

The ES6 API now has Reflux.Store for store creating as well as Reflux.Component for integration of those stores into React components.

@tebeco
Copy link

tebeco commented Mar 9, 2017

And Reflux.Component does not build with Typescript because @types/reflux does not include it

import * as Reflux from "reflux";

if i "Go to definition" of that "reflux" this is the output :

// Type definitions for RefluxJS 0.4
// Project: https://github.com/reflux/refluxjs
// Definitions by: Maurice de Beijer <https://github.com/mauricedb>
// Definitions: https://github.com/DefinitelyTyped/DefinitelyTyped

export as namespace Reflux;

interface StoreDefinition {
    listenables?: any[];
    init?: Function;
    getInitialState?: Function;
    [propertyName: string]: any;
}

interface ListenFn {
    (...params: any[]): any;
    completed: Function;
    failed: Function;
}
interface Listenable {
    listen: ListenFn;
}

interface Subscription {
    stop: Function;
    listenable: Listenable;
}

interface Store {
    hasListener(listenable: Listenable): boolean;
    listenToMany(listenables: Listenable[]): void;
    validateListening(listenable: Listenable): string;
    listenTo(listenable: Listenable, callback: Function, defaultCallback?: Function): Subscription;
    stopListeningTo(listenable: Listenable): boolean;
    stopListeningToAll(): void;
    fetchInitialState(listenable: Listenable, defaultCallback: Function): void;
    trigger(state: any): void;
    listen(callback: Function, bindContext: any): Function;
}

interface ActionsDefinition {
    [index: string]: any;
}

interface Actions {
    [index: string]: Listenable;
}

export function createStore(definition: StoreDefinition): Store;

export function createAction(definition?: ActionsDefinition): any;

export function createActions(definitions: ActionsDefinition | string[]): any;

export function connect(store: Store, key?: string): void;
export function listenTo(store: Store, handler: string): void;
export function setState(state: any): void;

export function ListenerMixin(): any;

@BryanGrezeszak
Copy link
Contributor

BryanGrezeszak commented Mar 9, 2017

@tebeco

Type definitions for RefluxJS 0.4

Their definitions seem a little out of date. We're currently on 6.4.1. Their definitions are for 0.4.x.

There is an open issue here on Reflux for anyone that wishes to help maintain typescript definitions.

If you'd prefer that the definitions on that repo be kept up, then an issue on that repo would be appropriate.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

6 participants