You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多
文档控制台
免费开始使用

React(Expo)与React Native项目共享代码及创建共享库的最简方案咨询

Hey there! Awesome plan—sharing code between React web and React Native is a fantastic way to keep your brand consistent and avoid repeating work. Let’s walk through the simplest, most straightforward implementation that fits your exact project structure request.

Step 1: Set Up Your Project Structure

First, lay out your folders exactly as you described. This keeps everything organized and makes it easy to distinguish shared vs platform-specific code:

your-project/
├── web/                # Your React web app (use Create React App or Vite)
│   ├── src/
│   └── package.json
├── mobile/             # Your React Native app (Expo or React Native CLI works)
│   ├── src/
│   └── package.json
└── shared/             # All shared code lives here
    ├── components/
    │   └── Button/     # Shared Button component folder
    └── constants/      # Shared brand assets
Step 2: Create Shared Brand Constants

Put all your brand-specific values (like colors, font weights, or spacing) in the shared/constants folder. This ensures your brand stays consistent across both platforms without duplication.

Create shared/constants/brand.js:

export const BRAND_COLORS = {
  primary: "#2563eb",   // Your main brand blue
  secondary: "#10b981", // A complementary green
  error: "#ef4444",     // Error state red
  white: "#ffffff",
};

export const FONT_WEIGHTS = {
  regular: 400,
  bold: 700,
};
Step 3: Build the Shared Button Component

The key here is separating shared logic/props from platform-specific rendering. We’ll use React’s platform-specific file suffixes (.web.jsx and .native.jsx) to let each platform load its own button implementation, while keeping the core logic shared.

3.1 Shared Button Logic File

Create shared/components/Button/Button.jsx—this is the "shell" that handles shared props, logic, and passes data to the platform-specific button:

import { BRAND_COLORS } from "../../constants/brand";
// Dynamically import the platform-specific button (React/React Native auto-detects suffixes)
import ButtonImpl from "./Button.platform";

export default function Button({
  children,
  variant = "primary",
  onPress,
  ...rest
}) {
  // Shared logic: Get the correct brand color based on the variant
  const bgColor = BRAND_COLORS[variant];

  // Shared handler: Wrap onPress to add any cross-platform logic (e.g., loading states)
  const handlePress = () => {
    if (onPress) onPress();
  };

  // Pass shared values and props to the platform-specific button
  return (
    <ButtonImpl
      bgColor={bgColor}
      onPress={handlePress}
      {...rest}
    >
      {children}
    </ButtonImpl>
  );
}

3.2 Web-Specific Button Rendering

Create shared/components/Button/Button.web.jsx—this uses the standard HTML <button> element:

import { BRAND_COLORS, FONT_WEIGHTS } from "../../constants/brand";

export default function ButtonWeb({ bgColor, onPress, children, ...rest }) {
  return (
    <button
      style={{
        backgroundColor: bgColor,
        color: BRAND_COLORS.white,
        padding: "10px 20px",
        border: "none",
        borderRadius: "6px",
        cursor: "pointer",
        fontSize: "16px",
        fontWeight: FONT_WEIGHTS.bold,
      }}
      onClick={onPress}
      {...rest}
    >
      {children}
    </button>
  );
}

3.3 Native-Specific Button Rendering

Create shared/components/Button/Button.native.jsx—this uses React Native’s TouchableOpacity (the standard native button component):

import { TouchableOpacity, Text, StyleSheet } from "react-native";
import { BRAND_COLORS, FONT_WEIGHTS } from "../../constants/brand";

export default function ButtonNative({ bgColor, onPress, children, ...rest }) {
  return (
    <TouchableOpacity
      style={[styles.button, { backgroundColor: bgColor }]}
      onPress={onPress}
      {...rest}
    >
      <Text style={styles.text}>{children}</Text>
    </TouchableOpacity>
  );
}

const styles = StyleSheet.create({
  button: {
    paddingVertical: 10,
    paddingHorizontal: 20,
    borderRadius: 6,
  },
  text: {
    color: BRAND_COLORS.white,
    fontSize: 16,
    fontWeight: FONT_WEIGHTS.bold,
    textAlign: "center",
  },
});
Step 4: Use the Shared Component in Web & Mobile

To make importing easy, add the shared folder as a local dependency in both your web and mobile projects:

  1. In web/package.json, add:
    "dependencies": {
      // ... your existing web dependencies
      "shared": "file:../shared"
    }
    
  2. In mobile/package.json, add the same line.

Now you can import the shared Button component in either project like this:

Web Example (web/src/App.jsx)

import Button from "shared/components/Button/Button";

function App() {
  return (
    <div style={{ padding: "2rem" }}>
      <Button variant="primary" onPress={() => alert("Web primary button clicked!")}>
        Web Primary Button
      </Button>
      <Button variant="secondary" onPress={() => alert("Web secondary button clicked!")} style={{ marginTop: "1rem" }}>
        Web Secondary Button
      </Button>
    </div>
  );
}

export default App;

Native Example (mobile/App.jsx)

import { View, StyleSheet } from "react-native";
import Button from "shared/components/Button/Button";

export default function App() {
  return (
    <View style={styles.container}>
      <Button variant="primary" onPress={() => alert("Native primary button clicked!")}>
        Native Primary Button
      </Button>
      <Button variant="secondary" onPress={() => alert("Native secondary button clicked!")} style={{ marginTop: 16 }}>
        Native Secondary Button
      </Button>
    </View>
  );
}

const styles = StyleSheet.create({
  container: {
    flex: 1,
    justifyContent: "center",
    alignItems: "center",
    padding: 20,
  },
});
Quick Tips for Smooth Sharing
  • Keep shared code platform-agnostic: Only put logic, constants, or component shells in shared—avoid anything that relies on web APIs (like window) or native APIs (like Alert directly) unless you wrap them in platform-specific modules.
  • Expo users win: Expo automatically detects .web.jsx and .native.jsx suffixes, so no extra config is needed. For React Native CLI, you might need to tweak your Babel config to handle platform files.
  • Add TypeScript (optional but helpful): If you use TypeScript, you can define shared prop types in the shared folder to ensure consistency across platforms.

内容的提问来源于stack exchange,提问作者Vishal Avalani

火山引擎 最新活动