Haxe React native development stack

Tech stack for mobile apps with Haxe, Redux and React native

View the Project on GitHub zabojad/haxe-react-native-stack

Designing your store state shape

The design of your state can have a great impact on several aspects of your project:

Let’s take the example of an app where we want to add some optional pictures gallery.

This feature will add:

This feature is not central to our app and we want it to be “like a brick” we can add or remove at compilation.

Let’s start with the state:

package myapp.state;

// ...

typedef GalleryState = {
    pictures:Array<String>
}

// and its corresponding reducer
class GalleryRdcr implements IReducer<GalleryAction, GalleryState> {
    public function new() {}

    public var initState:GalleryState = {
        pictures: [],
    }

    public function reduce(state:GalleryState, action:GalleryAction):GalleryState {
        var partial:Partial<GalleryState> = switch(action) {
            case ReceivePictures(ps):
                {pictures:state.pictures.concat(ps)};

            case ClearGallery:
                initState;
            
            // ...
        }
        return Object.assign({}, state, partial);
    }
}

Add it to your store creation logic:

// src/myapp/App.hx
// ...

#if withGallery
import myapp.state.GalleryState;
#end

    // ...

    private function initStore() : Store {

        // here we give the shape our store state
        var rootReducer = Redux.combineReducers({
            config: mapReducer(ConfigAction, new ConfigRdcr()),
            status: mapReducer(StatusAction, new StatusRdcr()),
            intl: mapReducer(IntlAction, new IntlRdcr()),
            session: mapReducer(SessionAction, new SessionRdcr()),
#if withGallery
            gallery: mapReducer(GalleryAction, new GalleryRdcr()),
#end
        });

        // ...
        
        return createStore(rootReducer, null, middleware);
    }

    // ...

Notice the conditional compilation instructions. If our .hxml file contains the -D withGallery option, the Gallery feature will be included in our app. Otherwise, it won’t.

Now, define the GalleryAction enum:

// src/myapp/action/GalleryAction.hx
package myapp.action;

enum GalleryAction {
    ReceivePictures(pics:Array<String>);
    ClearGallery;
}

The “controller” part of the feature as a GalleryThunk:

// src/myapp/action/thunk/GalleryThunk.hx
package myapp.action.thunk;

import redux.Redux.Dispatch;
import redux.thunk.Thunk.Action;
// ...
import myapp.state.State;
//...

class GalleryThunk {

    static public function fetchUserPictures(user:myapp.dto.User) {
        return Action(function(dispatch:Dispatch, getState:Void->State) {
            myapp.srv.MySrvApi.fetchUserPics(
                user,
                function(pics:Array<String>) {
                    dispatch(ReceivePictures(pics));
                }
            );
        });
    }

    //...
}

And finally, our Gallery component:

// myapp.view.screen;

import react.Partial;
// ...

import redux.react.ReactRedux;
// ...
import myapp.action.thunk.GalleryThunk; 
import myapp.state.GalleryState; 
// ...

typedef GalleryPropsPublicProps = {
    user:myapp.dto.User
}
typedef GalleryRdxProps = {
    gallery:myapp.state.GalleryState,
    fetchPics:Void->Void
}
typedef GalleryProps = {
    > GalleryPropsPublicProps,
    > GalleryRdxProps,
}

class Gallery extends ReactComponentOfProps<GalleryProps> {

    static public var Connected = ReactRedux.connect(mapStateToProps,mapDispatchToProps)(Gallery);

    static function mapStateToProps(state:State,ownProps:GalleryPropsPublicProps):Partial<GalleryRdxProps> {
        return {
            gallery: st.gallery
        }
    }
    static function mapDispatchToProps(dispatch:Dispatch,ownProps:GalleryPropsPublicProps):Partial<GalleryRdxProps> {
        return {
            fetchPics: function(){
                dispatch(GalleryThunk.fetchUserPictures(ownProps.user));
            }
        }
    }

    static function _keyExtractor(v:{item:String,index:Int}):String{
        return v.item;
    }

    override function componentWillMount(){
        props.fetchPics();
    }

    function renderPicture(v:{item:String,index:Int}){
        return jsx('
            <View>
                <Image
                    style=
                    source=
                />
            </View>
        ');
    }
    
    override function render(){
        return jsx('
            <View>
                <Text>User gallery:</Text>
                <FlatList
                    data={[props.gallery.pictures]}
                    renderItem={renderPicture}
                    keyExtractor={_keyExtractor}
                />
            </View>
        ');
    }
}

We’re done. We’ve add a feature that is testable independantly from the rest of the app and can be added/removed with a simple compilation file:

// app.dev.android.hxml

-D android 
# remove it if you don't want the gallery feature
-D withGallery
-js bin/App.android.js
app.dev.common.hxml