I'm planning a new cross‑platform mobile project using React Native and want to get the community's take on the best overall architecture. Should I favor a feature‑based folder layout or keep things more domain‑driven? How do you usually handle state management – Redux, Context API, or something else – without over‑complicating the code? Any thoughts on organizing reusable UI components, handling platform‑specific code, and setting up testing pipelines early on? Looking for practical tips that work at scale rather than quick hacks. How do you balance flexibility with maintainability in a growing codebase? Your experiences would be really helpful.
Seeking advice on structuring a scalable React Native codebase for cross‑platform apps
👁️ 49 görüntüleme💬 2 cevap❤️ 0 beğeni
2 Cevap
I’ve found a hybrid approach works best – start with a feature‑based folder structure for quick visibility, then gradually pull out truly domain‑wide pieces into their own modules and keep Redux Toolkit (or RTK Query) for the heavy lifting while sprinkling Context for tiny, local states 🚀. For platform‑specific UI I just drop `*.ios.tsx`/`*.android.tsx` files in the same component folder, wire up Storybook and Jest early on, and hope my rookie instincts don’t explode the build 😅.
I’ve been through a few RN launches, and the setup that scales best for me is a hybrid layout: keep a top‑level `src` split by **feature** (e.g., `auth`, `profile`, `checkout`) but inside each feature have subfolders for `ui`, `state`, and `services`. That gives you the mental model of “all things related to a screen live together” while still letting you group reusable pieces (icons, buttons, theming) under a shared `components` folder that’s referenced by the features. For domain‑wide concerns like networking, analytics, or theming, a separate `core` or `shared` layer sits alongside the feature folders.
For state, I start with **Redux Toolkit** + RTK Query for any async data. It adds a bit of boilerplate, but the slice‑based API keeps things modular and you avoid the “prop‑drilling” nightmare that Context can become once the app grows. If a feature is completely isolated and only needs local UI state, I fall back to the built‑in `useState`/`useReducer` or a lightweight Context, but I never mix the two patterns in the same screen. Platform‑specific code lives in the same folder using the `.ios.tsx` / `.android.tsx` convention; when the differences are minor I just use `Platform.select`, otherwise I create a separate component (e.g., `Button.ios.tsx` & `Button.android.tsx`) and re‑export it from an index file.
Testing early on saves a lot of pain: I wire up **Jest** for unit tests of slices, utils, and pure components, and add **React Native Testing Library** for rendering tests. For integration/E2E I prefer **Detox** because it runs on both simulators and real devices without much extra config. Keeping the test files next to the code (`MyComponent.test.tsx`) makes it easy to spot missing coverage as the tree expands.
The key balance is to enforce a clear contract between layers – UI only consumes props from its slice, services are pure functions that return promises, and shared components have no business logic. When the contract is respected, you can refactor a feature without worrying about ripple effects, and the codebase stays maintainable even as new screens and platforms are added.