kenwheeler / mcfly

Flux architecture made easy
BSD 3-Clause "New" or "Revised" License
762 stars 46 forks source link

Possible to use without mixins? #48

Open elliotlings opened 9 years ago

elliotlings commented 9 years ago

I'm fairly new to React. Just wondered what the use of mixins provide and whether there's a way around them so I can use ES6 classes?

tomatau commented 9 years ago

Here's a quick store listener HOC you can use instead of McFly store mixin.

import React from 'react/addons';

const StoreListener = (stores, getStateFromStores, Component) =>
  React.createClass({

    getInitialState(){
      return getStateFromStores(this.props)
    },

    componentDidMount() {
      stores.forEach(store=>
        store.addChangeListener(this.handleStoreChanged)
      )
    },

    componentWillUnmount(){
      stores.forEach(store=>
        store.removeChangeListener(this.handleStoreChanged)
      )
    },

    render() {
      return <Component {...this.props} {...this.state} />;
    },

    handleStoreChanged(){
      this.isMounted() && this.setState(getStateFromStores(this.props))
    }

  })

export default StoreListener;

Use like so:

const Thing = StoreListener(

  [ UserStore, AnotherStore ],

  ()=>({
    isLoggedIn: UserStore.isLoggedIn(),
    thingIsEmpty: AnotherStore.isEmpty()
  }),

  class Thing extends React.Component {

    render(){
      return (
        <div id="wrapper">
          <Loader />
          {(this.props.isLoggedIn && !this.props.thingIsEmpty) ? <SecretStuff/> : null}
        </div>
      )
    }

  }
)
export default Thing;

There's further improvements to be made to this HOC such as performance improvements around props and copying static component properties etc...

hekar commented 9 years ago

Anything planned for supporting ES6 classes in McFly?

samhunta commented 9 years ago

This is more of a React issue right? React now supports ES6

tomatau commented 9 years ago

I've been using this recently

import React from 'react/addons';
import _ from 'lodash';

const StoreListener = _.curry(
  (stores, getStateFromStores, statics, Component) =>
    React.createClass({

      displayName: `${Component.name}StoreListener`,

      statics,

      propTypes: Component.propTypes,

      getDefaultProps: ()=>Component.defaultProps,

      getInitialState(){
        return getStateFromStores(this.props)
      },

      componentDidMount() {
        stores.forEach(store=>
          store.addChangeListener(this.handleStoreChanged)
        )
      },

      componentWillUnmount(){
        stores.forEach(store=>
          store.removeChangeListener(this.handleStoreChanged)
        )
      },

      render() {
        return <Component {...this.props} {...this.state} />;
      },

      handleStoreChanged(){
        this.isMounted() && this.setState(getStateFromStores(this.props))
      }

    })
)
export default StoreListener;

This works as both a HOC or a decorator because of the curry... you just need to make sure all 4 args are passed through..

Use it as a class decorator like so:

@StoreListener(
  [ ItemsStore ],
  ()=>({
    items: ItemsStore.get()
  }),
  {
    willTransitionTo(){
      // static method here, as can't copy static methods from class definition so defining them separately here
    }
  }
)
class ItemsCtrl extends React.Component {

  render() {
    const { items } = this.props;
    return (
      // stuff
    );
  }

}
kenwheeler commented 9 years ago

@tomatau that's awesome

elliotlings commented 9 years ago

Thanks for the help, peeps.

tomatau commented 9 years ago

No probs, I'm tempted to open source my ES6/7 style version of McFly that I use for in-house projects.. new repository or a PR for v2?

It might be a little redundant now with the progress that other "Flux" frameworks and Relay are making though

jcperez-ch commented 8 years ago

@tomatau @kenwheeler that is a great idea, I know this project has been a bit abandoned but I believe is fantastic. McFly has a lot of future and we can start implementing ideas,patterns and practices from redux, so it can compete with it using the EventEmmitter approach.

I'll fork as well and try to constantly PR more features like @tomatau decorator :+1:

Don't give up guys!

tomatau commented 8 years ago

Interesting idea @jcperez-ch -- what do you see as benefits for EventEmmitter approach over a functional synchronous approach used in redux?

jcperez-ch commented 8 years ago

I need to be honest here @tomatau I don't see benefits over the pure functional synchronous way redux spreads the state over components. That would be the single principle of redux that could be cornered by mcFly, but we can have asynchronous events that can change a single state for the application and we can synchronize (via reducers) the state changes as well.

You can see my branch here: https://github.com/jcperez-ch/mcfly/tree/reduxify_mcfly

I created a guide in another place in another world on how to use reducers and single state in mcFly, so I will add it as example and in the readme.md in my next commit.