简体   繁体   中英

How to create new reactjs and react-native project and organise the source code repository?

We can use this CLI command to create a new React-Native project:

react-native init AwesomeProject

We can use npx to create a new ReactJS project :

npx create-react-app my-app

What if I would like to use monorepo to share code between React-Native and ReactJS? (sharing those besides UI rendering code)

Option:

1) Create a new React-Native Project and import ReactJs files and folders manually? (Not sure how to do it, eg how to manage package.json and node_modules)

2) Create a new ReactJS Project and import React-Native files and folders manually? (Not sure how to do it, eg how to manage package.json and node_modules)

3) Using 3 repo (one for react-native, one for reactjs web and one for shared code) and we will import the shared code as npm packages in React-Native repo and ReactJs repo respectively. -- wanted to use this approach but I can't upload any npm packages even private one due to limitation by client.

Any other better/more common suggestions?

Update:

According to this tutorial , we can create our own local npm packages. However, if the package is updated (eg if we change some redux code), we need to run "npm pack" and "npm install../package-name-0.0.0.tgz" everytime. It will be time consuming. Is this the correct way of updating local package?

You can read my article about Share React Native components to multiple projects , which I suggest 3 ways for sharing code between projects.

My case was almost exactly as yours. I have a React Native project , a ReactJS web app , a shared Library and the server code which is Google Cloud Firebase Functions for nodejs. I went with the third method using Babel and Metro Bundler .

Method 1: Git for npm packages

We can create a git repo for our Library directory and then having NPM to install it directly from the repo. That will work, but we'll have to synchronize (commit/push) git repos after every change to the Library which is not so practical for working on local projects. Check this Q/A for more details How to install an npm package from GitHub directly?.

Method 2: Git submodules

Git submodules is a really nice way to have shared code inside a project that it needs to be changed often. I was looking for this solution and I was excited when I saw the potential of using this method, but then I thought that I am not so familiar with git submodules and of course didn't want to do anything and mess with the git repos and then having some hard time fixing git issues. If you are familiar using git submodules, then this is the way to go.

Method 3 (Babel, Metro): Using.babelrc module resolver plugin for React with Metro Bundler resolver for React Native

We can use the module-resolver package to configure directory aliases. We create both projects normally for RN and ReactJS and we initialize the firebase functions project, the Library does not has to be initialized as a nodejs project:

Directory structure:

MyProject-RNApp\
MyProject-ReactJSApp\
MyProject-Backend\
MyProject-Library\

Every package that is used, it has to be installed for the project that it will be used. For example, if we have a common function inside our library that uses validator , then we'll have to npm install that package for each node project that uses it.

Configure module-resolver for all Babel projects

First inside .babelrc we define the aliases for each directory. Here we can add directories that are outside of the RN app root directory:

{
  "presets": ["module:metro-react-native-babel-preset"],
  "env": {
    "production": {
      "plugins": ["transform-remove-console", "react-native-paper/babel"]
    }
  },
  "plugins": [
    "@babel/plugin-transform-runtime",
    [
      "module-resolver",
      {
        "root": ["./"],
        "extensions": [".js", ".jsx", ".ts", ".tsx"],
        "stripExtensions": [".js", ".jsx", ".ts", ".tsx"],
        "alias": {
          "@components": "./components",
          "@screens": "./screens",
          "@utils": "./utils",
          "@data": "./data",
          "@assets": "./assets",
          "@app": "./",
          "@myprojectlib": "../MyProject-Library",
          "@myprojecti18n": "../MyProject-Library/i18n/rn",
          "@myprojectbackend": "../MyProject-Backend/firebase/functions",
        }
      }
    ]
  ]
}

Here you have to be careful with the paths, but relative paths also work well. Also you don't have to add aliases for each package when configuring the RN project because you'll do that below using the Metro Bundler configuration. You'll have to create package aliases just for the ReachJS and Backend Babel projects:

{
  "plugins": [
    [
      "module-resolver",
      {
        "root": "../",
        "alias": {
          "@app": "./resources/js/app",
          "@graphics": "./resources/js/app/graphics",
          "@styles": "./resources/js/app/styles",
          "@components": "./resources/js/app/components",
          "@screens": "./resources/js/app/screens",
          "@panels": "./resources/js/app/panels",
          "@stores": "./resources/js/app/stores",
          "@utils": "./resources/js/app/utils",
          "@dialogs": "./resources/js/app/dialogs",
          "@data": "./resources/js/app/data",
          "@myprojectlib": process.env.MYPROJECT_LIBRARY_PATH,
          "@myprojectbrain": process.env.MYPROJECT_BRAIN_PATH,
          "@myprojecti18n": `${process.env.MYPROJECT_LIBRARY_PATH}/i18n`,

          // Add module paths for external myproject, else it wont work!

          "react": "./node_modules/react",
          "react-flag-icon-css": "./node_modules/react-flag-icon-css",
          "react-hot-loader": "./node_modules/react-hot-loader",
          "react-localization": "./node_modules/react-localization",
          "prop-types": "./node_modules/prop-types",
          "@material-ui/pickers": "./node_modules/@material-ui/pickers",
          "@material-ui/core": "./node_modules/@material-ui/core",
          "classnames": "./node_modules/classnames",
          "qs": "./node_modules/qs",
          "luxon": "./node_modules/luxon",
          "@babel/runtime": "./node_modules/@babel/runtime",
        }
      }
    ]
  ]
}

Then for RN we create a metro.config.js file in the project root directory in order to configure the RN Metro Bundler to resolve each directory and shared npm package like this:

var path = require("path");
var config = {
  projectRoot: path.resolve(__dirname),
  watchFolders: [
    // Let's add the root folder to the watcher
    // for live reload purpose
    path.resolve(__dirname, "../MyProject-Library"),
    path.resolve(__dirname, "../MyProject-Backend")
  ],
  resolver: {
    sourceExts: ['js', 'jsx', 'ts', 'tsx'],
    extraNodeModules: {
      // Here I reference my upper folder
      "myprojectlib": path.resolve(__dirname, "../MyProject-Library"),
      "myprojectbackend": path.resolve(__dirname, "../MyProject-Backend/firebase/functions"),

      // Important, those are all the dependencies
      // asked by the "../MyProject-Library" but which
      // are not present in the ROOT/node_modules
      // So install it in your RN project and reference them here

      // "expo": path.resolve(__dirname, "node_modules/expo"),
      // "lodash.merge": path.resolve(__dirname, "node_modules/lodash.merge"),
      "dinero.js": path.resolve(__dirname, "node_modules/dinero.js"),
      "luxon": path.resolve(__dirname, "node_modules/luxon"),
      "validator": path.resolve(__dirname, "node_modules/validator"),
      "react-native-reanimated": path.resolve(__dirname, "node_modules/react-native-reanimated"),
      "react-native-gesture-handler": path.resolve(__dirname, "node_modules/react-native-gesture-handler"),
      "react-native-vector-icons": path.resolve(__dirname, "node_modules/react-native-vector-icons"),
      "react-native-navigation": path.resolve(__dirname, "node_modules/react-native-navigation"),
      "react-native-firebase": path.resolve(__dirname, "node_modules/react-native-firebase"),

      "prop-types": path.resolve(__dirname, "node_modules/prop-types"),
      "react-native": path.resolve(__dirname, "node_modules/react-native"),
      "react": path.resolve(__dirname, "node_modules/react"),
      "@babel/runtime": path.resolve(__dirname, "node_modules/@babel/runtime"),
      "@jsassets": path.resolve(__dirname, "./jsassets"),
      "@data": path.resolve(__dirname, "./data"),
      "@components": path.resolve(__dirname, "./components"),
      "@app": path.resolve(__dirname),
    }
  }
}
module.exports = config;

We have to define each shared package and common directory in resolver.extraNodeModules ; We have to define even react and react-native in order for the shared code inside our library to be able to have components and access React and RN features.

When we run yarn start (or npm run start ), we'll see Metro Bundler looking for JS files inside all fc + RNApp, Library, Backend :

Metro Bundler 加载

You should checkout Learna .

Where you start with root folder having

/main (root directory)
/main/native (react-native project)
/main/state  (possibly redux or mobx) 
/main/web    (create-react-app) 
/main/VR     (in case you want to consume state here) 

Now using this structure you could maintain a monorepo and have your state logic seperate and multiple UI consuming that state logic.

You can have 3 repositories (as mentioned in your message).

Example:

react-native
react
common

Each repository will contain a package.json file with a different name (essential, eg, root directory name = package.json name).

After that, you can do a npm link../common from the react-native project, and the react project, npm link will symlink the common package into the other repositories and you will be able to use it like any other libraries, this is the common way to develop a new library.

Bonus, because this is a symlink, every changes made onto the common package will be reflected on the react-native (might need to manually refresh as react-native does not refresh the node_modules ) and the react projects.

You have to run npm link again every time you are running npm install

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM