Search code examples
react-nativecreate-react-appreact-native-web

Dealing with 3rd parties in create-react-app with react-native-web


We are trying to use the same React components both in mobile (react-native) and in web (create-react-app).

It's is working out pretty well so far thanks to react-native-web (configuration below).

However react-native-vector-icons, which we use extensively in the react-native project, does not compile with react-native-web. This means any component which uses a component with vector-icons will also need a specific web version. Having web specific versions of components effects maintenance.

Is there a known way, without having to eject create-react-app configuration, to deal with 3rd parties such as react-native-vector-icons in web?

import {createIconSetFromIcoMoon} from "react-native-vector-icons";

import Font from './Font.json';
export default createIconSetFromIcoMoon(UbeyaFont);

Things we've thought of so far: we are currently looking into two ideas:

  • Environment based import:

    # Pseudo code:
    # if mobile import this, otherwise import that.
    

    We're not sure whether this kind of dynamic importing is possible

  • Webpack configuration which is injected into node_modules/react-scripts. Not elegant but with a gulp watch which we have anyway we can make sure the configuration is always there.

Any ideas or thoughts are appreciated.

Configuration: We've built a new private NPM package which holds all the React components and by using gulp watch that copies the package to both the mobile and web projects we save the trouble of constantly npm publishing and yarning during development (the only drawback Webstorm's indexing process).


Solution

  • We ended up using a gulp script to overwrite webpack. The stages are:

    1) Build replacement packages

    We built a web version for each React Native 3rd party. For react-native-vector-icons it turned out to be quite simple:

        import React from 'react';
    
        export const Icon = (props) => <div className="material-icons" 
                                            style={{color:props.color}}>
            {props.name}
        </div>;
    
    
        export default Icon;
    

    2) Adjust the Webpack configuration

    Using gulp we overwrite the create-react-app webpack configuration with:

     resolve: {
            modules: ...
            alias: {
                "babel-runtime": path.dirname(
                    require.resolve("babel-runtime/package.json")
                ),
                "react-native": "@ourcompany/react-native-web",
                "react-native-vector-icons": "@ourcompany/react-native-vector-icons",
       ...
    

    The gulp task script:

    gulp.task("copyWebpack", function() {
        return gulp
            .src(["package.json", "src/_webpack/*.js"])
            .pipe(
                gulp.dest(
                    path.resolve(pathWeb + "/node_modules/react-scripts/config")
                )
            );
    });
    

    Note:

    • Ejecting create-react-app's configuration is cleaner, however it means you'll need to maintain the configuration and we preferred leaving the configuration as is and overwriting it during the build process.
    • You'd notice we have overwritten react-native-web itself. More on this in the next optional step.

    3) Extend react-native-web (optional)

    If you are using components which react-native-web does not yet support, you would want to build packages for them and extend react-native-web so your web version will work. Simply create a new package @yourcompany/react-native-web and generate an index.js in which import the components that do exist in react-native-web and reference the ones you've built. Note that when we built our project react-native-web didn't have a FlatList or a SectionList and now (October 2018) it has both (Modal is still missing :-)).

    import {
        StyleSheet as _StyleSheet,
        View as _View,
        Text as _Text,
        Image as _Image,
        I18nManager as _I18nManager,
        AsyncStorage as _AsyncStorage,
        Platform as _Platform,
        Linking as _Linking,
        ActivityIndicator as _ActivityIndicator,
        ListView as _ListView,
        Modal as _Modal,
        Picker as _Picker,
        RefreshControl as _RefreshControl,
        TextInput as _TextInput,
        Touchable as _Touchable,
        TouchableHighlight as _TouchableHighlight,
        TouchableNativeFeedback as _TouchableNativeFeedback,
        TouchableOpacity as _TouchableOpacity,
        TouchableWithoutFeedback as _TouchableWithoutFeedback,
        Dimensions as _Dimensions,
        Animated as _Animated,
        ScrollView as _ScrollView,
        SafeAreaView as _SafeAreaView,
        BackHandler as _BackHandler,
        Switch as _Switch,
        NetInfo as _NetInfo,
        AppState as _AppState,
    
        ColorPropType as _ColorPropType,
    } from 'react-native-web';
    
    import {SectionList as _SectionList} from './SectionList';
    import {FlatList as _FlatList} from './FlatList';
    import {Alert as _Alert} from './Alert';
    import {WebView as _WebView} from './WebView';
    import {Share as _Share} from './Share';
    import {PermissionsAndroid as _PermissionsAndroid} from './PermissionsAndroid';
    import {ActionSheetIOS as _ActionSheetIOS} from './ActionSheetIOS';
    import {ToastAndroid as _ToastAndroid} from './ToastAndroid';
    
    export const StyleSheet = _StyleSheet;
    export const View = _View;
    export const Text = _Text;
    export const Image = _Image;
    export const I18nManager = _I18nManager;
    export const AsyncStorage = _AsyncStorage;
    export const Platform = _Platform;
    export const Linking = _Linking;
    export const ActivityIndicator = _ActivityIndicator;
    export const ListView = _ListView;
    export const Modal = _Modal;
    export const Picker = _Picker;
    export const RefreshControl = _RefreshControl;
    export const TextInput = _TextInput;
    export const Touchable = _Touchable;
    export const TouchableHighlight = _TouchableHighlight;
    export const TouchableNativeFeedback = _TouchableNativeFeedback;
    export const TouchableOpacity = _TouchableOpacity;
    export const TouchableWithoutFeedback = _TouchableWithoutFeedback;
    export const Dimensions = _Dimensions;
    export const Animated = _Animated;
    export const ScrollView = _ScrollView;
    export const SafeAreaView = _SafeAreaView;
    export const BackHandler = _BackHandler;
    export const Switch = _Switch;
    export const NetInfo = _NetInfo;
    export const AppState = _AppState;
    
    export const Alert = _Alert;
    export const Share = _Share;
    export const WebView = _WebView;
    export const PermissionsAndroid = _PermissionsAndroid;
    export const ActionSheetIOS = _ActionSheetIOS;
    export const ToastAndroid = _ToastAndroid;
    
    export const SectionList = _SectionList;
    export const FlatList = _FlatList;
    
    export const ColorPropType = _ColorPropType;