GithubHelp home page GithubHelp logo

Comments (19)

kevinresol avatar kevinresol commented on July 21, 2024 2

You can further improve it by writing some macros to insert the __init__ function like:

@:build(ReactReduxMacro.connect(TodoList))
class VisibleTodoList {
    static function mapStateToProps(state) {
        return {...}
    }

    static function mapDispatchToProps(dispatch) {
        return {...}
    }
}

class ReactReduxMacro {
    public static function connect(e:Expr) {
        var cls = Context.getLocalClass().get();
        return Context.getBuildFields().concat([{
            access: [AStatic],
            name: '__init__',
            kind: FFun({
                args: [],
                expr: macro untyped $i{cls.name} = js.Lib.require('react-redux').connect(mapStateToProps, mapDispatchToProps)($e),
                ret: null,
            }),
            pos: Context.currentPos(),
        }]);
    }
}

from haxe-react.

elsassph avatar elsassph commented on July 21, 2024 1

I hope you realize that <${MyComp.redux()} /> (besides the syntax) is a terrible thing to do: the ReactRedux.connect function creates a whole new class extending MyComp. See http://github.com/reactjs/react-redux/blob/master/src/components/connect.js
You would end up creating a whole new class every time your render function runs.

That's the reason why the recommended pattern in JS is to completely replace MyComp with the new class that connect creates. But this pattern would be very difficult to use in Haxe because of the way classes are generated in some cases.

I didn't check what ReactIntl.injectIntl does but I've seen an example where they wrap a "pure render" function (stateless component) and you are suggesting that it could wrap also a class like ReactRedux.connect does.

Finally for the sake of exhaustivity, I've seen HOCs adding functions to the class prototype instead of extending the component with a new class. This is also pretty hard to support in typed languages.

A pattern that seem to work is:

class MyComp {
    static public var Connect = ReactRedux.connect(mapStateToProps)(MyComp);
    ...
}

Which you would use as: <MyComp.Connect/>.

That is nicely compatible with Haxe and easy to write manually. Potentially you could come up with a macro generating such Connect property.

from haxe-react.

zabojad avatar zabojad commented on July 21, 2024

Or... it should be up to the ReactRedux (or any other lib) extern to define its hoc factory metadata itself... That probably preferable as it would maybe allow a better checking from the compiler...

from haxe-react.

elsassph avatar elsassph commented on July 21, 2024

Some cases HOCs would difficult to reconcile with Haxe I think.

  • stateless function renderers would be simple enough but the macro generator doesn't support them,
  • old-school mixins mess would be tricky to solve using Haxe typesystem,
  • definition of custom classes (connect I think?) might work if es6 classes can extend Haxe ones. But then it might still be tricky to handle the necessary JS code generation for it.

The first step is to figure what Haxe code should be generated for such cases. Eg. if it's possible to write manually, then it can be generated using a macro - otherwise it's likely that it's not possible.

from haxe-react.

zabojad avatar zabojad commented on July 21, 2024

Hi there!

I do not understand all what you wrote... Maybe because I was not clear myself.

I'll try to sumarize the issue:

The HOC factories actually only inject logic/objects in the component props. No mixin here nor extension nor anything else...

When you do var MyReduxAwareComp = ReactRedux.connect(mapStateToProps)(MyComp), it actually only injects additional data or functions/callbacks in the props of MyComp to give MyReduxAwareComp.

The first step is to figure what Haxe code should be generated for such cases. Eg. if it's possible to write manually, then it can be generated using a macro - otherwise it's likely that it's not possible.

Yep, that makes sense to me.

So, if we stick to the ReactRedux example, here is what I currently do (and it works of course):

MyComp.hx:

typedef MyCompProps {
    data : Array<String>
}
class MyComp extends ReactComponentOf<MyCompProps, Dynamic, Dynamic> {

    static function mapStateToProps(state : Dynamic) {
        return {
            data: state != null ? state.data : []
        }
    }

    static public function redux() : Dynamic {
        return ReactRedux.connect(mapStateToProps)(MyComp);
    }

    override function render() {
        // (...)
    }
}

App.hx

class App {
    static public function main() {

        ReactDOM.render(jsx('
                <${MyComp.redux()} />
            '), 

            js.Browser.window.document.body);

    }
}

It would be great to have some nicer syntax that does not require to write this: <${MyComp.redux()} /> but this <MyComp />.

Is it clear to you? How would you see this with a macro/metadata/... ?

Note that the ReactRedux.connect factory is the most complex HOC factory I've seen until now. Usually, it's just a normal function that just takes your comp as parameter like this one:

class MyComp extends ReactComponentOf<Dynamic, Dynamic, Dynamic> {

    static public function intl() : Dynamic {
        return ReactIntl.injectIntl(MyComp);
    }

    // (...)
}

Note also that we need a system that allows "hoc factory chaining" on a single comp (some comp need to be passed through several different HOC factories to let them get features from several libs).

from haxe-react.

zabojad avatar zabojad commented on July 21, 2024

I hope you realize that <${MyComp.redux()} /> (besides the syntax) is a terrible thing to do: the ReactRedux.connect function creates a whole new class extending MyComp. See http://github.com/reactjs/react-redux/blob/master/src/components/connect.js
You would end up creating a whole new class every time your render function runs.

Gosh! Thanks for pointing this me out :s!

ReactIntl.injectIntl() also returns a wrapping component... I believe it will be the case most of the time with HOC factories...

Finally for the sake of exhaustivity, I've seen HOCs adding functions to the class prototype instead of extending the component with a new class. This is also pretty hard to support in typed languages.

I have not seen that yet. Also is this compatible with ES6? I haven't learnt ES6 yet but given that mixins are not possible with ES6, I would suspect this way of designing HOC factories would not work with ES6... If that's the case, we could consider this use case of HOC factories to be invalid and thus not support it...

A pattern that seem to work is:

class MyComp {
static public var Connect = ReactRedux.connect(mapStateToProps)(MyComp);
...
}
Which you would use as: <MyComp.Connect/>.

That is nicely compatible with Haxe and easy to write manually. Potentially you could come up with a macro generating such Connect property.

Yep, I agree it's the simplest and cleanest way so far... If it was just to generate this and nothing else, I think no macro is needed here...

However, maybe we could think of a macro that would:

  • generates this above,
  • extends the MyComp original props with the ones added by Redux (in this example)

So that in only one statement (a metadata actually) we enforce the Redux additional props to the MyComp props and "connect" it...

Or maybe that's not a good idea and it has not such a great value that it would require the pain of working on a macro for that...

What do you think?

from haxe-react.

elsassph avatar elsassph commented on July 21, 2024

You can still modify the prototype in ES6, and do mixins "manually". There just isn't a way using the class Foo extends Bar syntaxic sugar.

extends the MyComp original props with the ones added by Redux (in this example)

How do you know which props to add? I think you should just define those props as expected by your component even if it wasn't connected through redux.

Generally you want to avoid defining new types - it's badly handled by IDEs unless they are indirectly accessed.

from haxe-react.

fullofcaffeine avatar fullofcaffeine commented on July 21, 2024

@elsassph Maybe with the help of a custom js generator it'd be possible? It'd be pretty hardcore to do and maintain though. Not sure if worth it.

from haxe-react.

elsassph avatar elsassph commented on July 21, 2024

A custom JS generator would make the redux MyComp = connect(mapStateToProps)(MyComp) pattern possible (but a pain to do and maintain) but I believe a better thing to do would be to look into converting this connect function to Haxe semantics. eg. I believe a macro could effectively replace it.

from haxe-react.

kevinresol avatar kevinresol commented on July 21, 2024

I successfully use react-redux's connect() with the following code

class VisibleTodoList {
    static function mapStateToProps(state) {
        return {...}
    }

    static function mapDispatchToProps(dispatch) {
        return {...}
    }

    static function __init__()
        // kind of hack to assign something to a class, but works
        untyped VisibleTodoList = js.Lib.require('react-redux').connect(mapStateToProps, mapDispatchToProps)(TodoList);

}

class TodoList extends ReactComponent {
    override function render() {
        return jsx('...');
    }
}

from haxe-react.

elsassph avatar elsassph commented on July 21, 2024

This is starting to be clever, but that isn't really a scalable pattern of you want to heavily use HOCs.

from haxe-react.

Glidias avatar Glidias commented on July 21, 2024

What are the possible issues with this approach, seems to be fine if the end user is given the choice to either use the lower order component vs the higher order one in a Render.

from haxe-react.

fullofcaffeine avatar fullofcaffeine commented on July 21, 2024

Any updates on haxe-react HOC patterns? HOCs are not uncommon in a React codebase so having some utility or clear pattern to straightforwardly apply HOCs from within haxe-react would be nice. For now, the best examples of what @kevinresol has shown above.

from haxe-react.

elsassph avatar elsassph commented on July 21, 2024

Current recommended approach would be using @:jsxStatic:

@:jsxStatic(wrapped)
class VisibleTodoList {

    static function mapStateToProps(state) {
        return {...}
    }
    static function mapDispatchToProps(dispatch) {
        return {...}
    }

   static final wrapped = js.Lib.require('react-redux').connect(TodoList, mapStateToProps, mapDispatchToProps);
}

PS: it's better BTW to declare mapDispatchToProps as a static object.

from haxe-react.

kLabz avatar kLabz commented on July 21, 2024

There are with react-next https://github.com/kLabz/haxe-react/blob/next/doc/wrapping-with-hoc.md
Should be (mostly?) applicable to this lib as well, actually (see #102)

from haxe-react.

elsassph avatar elsassph commented on July 21, 2024

Oh actually @wrap. Undocumented so might as well not exist :D

from haxe-react.

kLabz avatar kLabz commented on July 21, 2024

Copying everything from the link above up to @:publicProps(TProps) part should cover basic usage for haxe-react.

from haxe-react.

elsassph avatar elsassph commented on July 21, 2024

Reorganised and completed the documentation about it - check the readme :)

from haxe-react.

kLabz avatar kLabz commented on July 21, 2024

Nice, readme looks a lot better like that 👍

from haxe-react.

Related Issues (20)

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.