Skip to content

Commit 37a91b8

Browse files
committed
feat: update the plan with re-render optimizations
1 parent 53a5ff6 commit 37a91b8

1 file changed

Lines changed: 71 additions & 0 deletions

File tree

brownfield-store-plan.md

Lines changed: 71 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -390,6 +390,77 @@ preBuild.dependsOn("generateBrownfieldStore")
390390
- [ ] Optimize re-renders on the JS side (selector support)
391391
- [ ] Zustand integration
392392
393+
## Re-render Optimization Plan
394+
395+
### Problem
396+
397+
Both JS and Swift currently re-render all subscribers when any property changes.
398+
399+
**JS**: `useBrownieStore` returns full state → any change triggers all consumers
400+
**Swift**: `@Published state` replaced wholesale → all `@UseStore` views re-render
401+
402+
### JavaScript Solution: Selector Hook
403+
404+
Add `useBrownieStoreSelector` that only re-renders when selected value changes:
405+
406+
```ts
407+
function useBrownieStoreSelector<K extends keyof BrownieStores, T>(
408+
key: K,
409+
selector: (state: BrownieStores[K]) => T,
410+
isEqual?: (a: T, b: T) => boolean
411+
): T;
412+
```
413+
414+
**Usage:**
415+
416+
```ts
417+
const counter = useBrownieStoreSelector('BrownfieldStore', (s) => s.counter);
418+
const user = useBrownieStoreSelector('BrownfieldStore', (s) => s.user);
419+
```
420+
421+
**Implementation:**
422+
423+
1. Store previous selected value in `useRef`
424+
2. On store change, run selector on new snapshot
425+
3. Compare with previous using `isEqual` (default: `Object.is`)
426+
4. Only trigger re-render if different
427+
428+
### Swift Solution: Selector Property Wrapper
429+
430+
Add `@UseStoreSelector` that subscribes to specific keypath:
431+
432+
```swift
433+
@propertyWrapper
434+
struct UseStoreSelector<State: BrownieStoreProtocol, Value: Equatable>: DynamicProperty {
435+
let keyPath: KeyPath<State, Value>
436+
@State private var value: Value
437+
438+
init(_ keyPath: KeyPath<State, Value>)
439+
var wrappedValue: Value { get }
440+
}
441+
```
442+
443+
**Usage:**
444+
445+
```swift
446+
@UseStoreSelector(\.counter) var counter: Int
447+
@UseStoreSelector(\.user) var user: String
448+
```
449+
450+
**Implementation:**
451+
452+
1. Use `@State` instead of `@StateObject` (no `ObservableObject` dependency)
453+
2. Listen to `BrownieStoreUpdated` notification
454+
3. Extract value via keyPath from C++ snapshot
455+
4. Only update `@State` if value differs (Equatable check)
456+
457+
### Tasks
458+
459+
- [ ] JS: Implement `useBrownieStoreSelector` hook
460+
- [ ] JS: Add `isEqual` option for custom comparison (objects/arrays)
461+
- [ ] Swift: Implement `@UseStoreSelector` property wrapper
462+
- [ ] Swift: Consider `@Observable` macro for iOS 17+ (automatic property tracking)
463+
393464
### Distribution
394465
395466
- [ ] Support xcframework packaging (iOS)

0 commit comments

Comments
 (0)