Understanding Doric's Architecture
Key Components
- Doric Core: The runtime engine that handles UI rendering and manages JS-native communication.
- Bridge: Handles bi-directional messaging between JavaScript and native Android/iOS components.
- Doric Modules: Native-side features exposed to JavaScript through the bridge (e.g., animations, navigation).
Common Integration Stack
Doric apps typically use a JavaScript-based logic layer (often bundled via Webpack) and embed native views via Android or iOS containers. Any desync or misconfiguration in this stack can cause erratic behavior and performance bottlenecks.
Common Troubleshooting Scenarios
1. Inconsistent Layout Rendering
Developers often report layout shifts or blank views, especially when using custom components or nested stacks.
Symptoms: Views render off-screen, overlapping, or disappear entirely
Root Causes:
- Incorrect layout params (e.g., missing
width
orheight
) - Improper use of Flexbox-style layout in nested stacks
- Delayed view inflation due to bridge latency
Fix:
- Ensure all views have explicit layout values
- Use
Stack
andFlexLayout
modules appropriately - Validate layout behavior in both Android and iOS native shells
2. Memory Leaks During Page Navigation
Doric uses retained JavaScript contexts and native references. If not disposed correctly, memory leaks can accumulate across page transitions.
Issue: Increasing memory usage on every page open/close
Diagnosis: Use Xcode Instruments or Android Profiler to trace retained views and JavaScript references.
Solution:
- Explicitly call
dispose()
on modules duringonDestroy()
- Avoid global state in JS unless managed by a singleton or store
- Use weak references in native extensions where applicable
3. JS-Native Communication Failures
Bridge failures can result in methods not being invoked or responses being dropped.
Console Warning: "CallNative failed: method not found: toast"
Common Causes:
- Mismatched method name or case sensitivity
- Missing
@DoricMethod
annotation on the native side - Failure to register modules with
registerModule()
Fix: Always confirm native methods are correctly annotated and registered before page launch. Use bridge logging to trace request and response pairs.
Diagnostics and Debugging
Enable Bridge Logs
Set DoricNative.setDebug(true)
in the Android/iOS host app to log all bridge messages, errors, and module dispatches.
Use Chrome DevTools with WebSocket Proxy
Doric supports remote debugging of JS logic. Use a WebSocket bridge to link the JS runtime to Chrome DevTools for breakpoints and variable inspection.
Native Profiler Usage
Use Android Profiler or Instruments to monitor FPS, memory usage, and thread contention to isolate rendering and memory issues.
Long-Term Architectural Recommendations
1. Separate Navigation State from UI Logic
Use a centralized store (like Redux or Vuex) to manage navigation and avoid coupling UI transitions with component logic.
2. Module Versioning
Define versioned module interfaces and validate compatibility during initialization. This prevents runtime crashes when updating the native app with stale JS code.
3. Hybrid Rendering Fallbacks
Use native fallback rendering for critical screens where performance is paramount or Doric modules are still maturing.
Best Practices
- Minimize use of global variables in JS context
- Dispose unused views explicitly on page transition
- Profile memory frequently during development
- Isolate third-party native libraries in their own modules
- Use defensive coding in native bridge handlers
Conclusion
Doric offers an elegant and lightweight bridge between native and JavaScript worlds, but at scale, it requires disciplined architecture, memory management, and observability. Developers must proactively handle lifecycle events, enforce layout consistency, and monitor bridge communication to maintain reliability. By adopting these strategies, enterprise teams can use Doric to deliver performant mobile experiences while retaining the flexibility of web technologies.
FAQs
1. Can Doric handle large-scale navigation stacks?
Yes, but only with disciplined memory and state management. Always dispose of pages explicitly and avoid keeping deep stacks in memory without navigation guards.
2. How do I debug Doric view rendering issues?
Use the Doric debugger in combination with layout logs on the native side. Log the full view hierarchy and ensure all dimensions are resolved before render.
3. Why is my JS module not invoking native methods?
Check for missing annotations, incorrect method names, or registration errors. Enable bridge logging to trace call paths and ensure correct parameter serialization.
4. Does Doric support code splitting?
Yes. Use bundlers like Webpack to split JS modules by route or feature. This reduces load time and memory footprint in large apps.
5. What are good alternatives for native-heavy screens?
For performance-critical screens, consider writing native views and embedding Doric pages selectively. This hybrid model gives more control without abandoning JS flexibility.