Skip to content

Commit 95ad710

Browse files
Marek Fořtnaqvitalha
andauthored
CellRendererComponent prop (#362)
* CellRendererComponent prop * Add CellContainer props documentation * added index prop details * remove logs * typo fix Co-authored-by: Talha Naqvi <talha.naqvi@shopify.com> Co-authored-by: Talha Naqvi <naqvitalha@gmail.com>
1 parent 6fb47ca commit 95ad710

File tree

9 files changed

+79
-8
lines changed

9 files changed

+79
-8
lines changed

CHANGELOG.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,8 @@ and adheres to [Semantic Versioning](http://semver.org/spec/v2.0.0.html).
1313
- https://github.com/Shopify/flash-list/pull/386
1414
- Change `overrideItemType` prop name to `getItemType`
1515
- https://github.com/Shopify/flash-list/pull/369
16+
- Added `CellRendererComponent` prop
17+
- https://github.com/Shopify/flash-list/pull/362
1618
- Added automatic height measurement for horizontal lists even when parent isn't deterministic
1719
- https://github.com/Shopify/flash-list/pull/409
1820

android/src/main/kotlin/com/shopify/reactnative/flash_list/AutoLayoutView.kt

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -51,7 +51,14 @@ class AutoLayoutView(context: Context) : ReactViewGroup(context) {
5151
* Performance: Sort is needed. Given relatively low number of views in RecyclerListView render tree this should be a non issue.*/
5252
private fun fixLayout() {
5353
if (childCount > 1) {
54-
val positionSortedViews = Array(childCount) { getChildAt(it) as CellContainer }
54+
val positionSortedViews: Array<CellContainer> = Array(childCount) {
55+
val child = getChildAt(it)
56+
if (child is CellContainer) {
57+
child
58+
} else {
59+
throw IllegalStateException("CellRendererComponent outer view should always be CellContainer. Learn more here: https://shopify.github.io/flash-list/docs/usage#cellrenderercomponent.")
60+
}
61+
}
5562
positionSortedViews.sortBy { it.index }
5663
alShadow.offsetFromStart = if (alShadow.horizontal) left else top
5764
alShadow.clearGapsAndOverlaps(positionSortedViews)

documentation/docs/fundamentals/usage.md

Lines changed: 33 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -93,6 +93,39 @@ estimatedItemSize: number;
9393

9494
---
9595

96+
### `CellRendererComponent`
97+
98+
Each cell is rendered using this element. Can be a React Component Class, or a render function. The root component should always be a `CellContainer` which is also the default component used. Ensure that the original `props` are passed to the returned `CellContainer`. The `props` contain the following properties:
99+
100+
- `onLayout`: Method for updating data about the real `CellContainer` layout
101+
- `index`: Index of the cell in the list, you can use this to query data if needed
102+
- `style`: Style of `CellContainer`, including:
103+
- `flexDirection`: Depends on whether your list is horizontal or vertical
104+
- `position`: Value of this will be `absolute` as that's how `FlashList` positions elements
105+
- `left`: Determines position of the element on x axis
106+
- `top`: Determines position of the element on y axis
107+
- `width`: Determines width of the element (present when list is vertical)
108+
- `height`: Determines height of the element (present when list is horizontal)
109+
110+
When using with `react-native-reanimated`, you can wrap `CellContainer` in `Animated.createAnimatedComponent` (this is similar to using `Animated.View`):
111+
112+
```ts
113+
const AnimatedCellContainer = Animated.createAnimatedComponent(CellContainer);
114+
return (
115+
<FlashList
116+
CellRendererComponent={(props) => {
117+
return (
118+
<AnimatedCellContainer {...props} style={...}>
119+
);
120+
}}
121+
/>
122+
);
123+
```
124+
125+
```ts
126+
CellRendererComponent?: React.ComponentType<any> | undefined;
127+
```
128+
96129
### `ItemSeparatorComponent`
97130

98131
Rendered in between each item, but not at the top or bottom. By default, `leadingItem` and `trailingItem` (if available) props are provided.
@@ -538,7 +571,6 @@ Param `animated` (`true` by default) defines whether the list should do an anima
538571

539572
The following props from `FlatList` are currently not implemented:
540573

541-
- [`CellRendererComponent`](https://reactnative.dev/docs/virtualizedlist#cellrenderercomponent)
542574
- [`columnWrapperStyle`](https://reactnative.dev/docs/flatlist#columnwrapperstyle)
543575
- [`debug`](https://reactnative.dev/docs/virtualizedlist#debug)
544576
- [`listKey`](https://reactnative.dev/docs/virtualizedlist#listkey)

fixture/ios/Podfile.lock

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -360,7 +360,7 @@ PODS:
360360
- React-jsi (= 0.68.1)
361361
- React-logger (= 0.68.1)
362362
- React-perflogger (= 0.68.1)
363-
- ReactNativePerformanceListsProfiler (0.0.10):
363+
- ReactNativePerformanceListsProfiler (0.0.11):
364364
- React-Core
365365
- RNFastImage (8.5.11):
366366
- React-Core
@@ -628,7 +628,7 @@ SPEC CHECKSUMS:
628628
React-RCTVibration: 9e344c840176b0af9c84d5019eb4fed8b3c105a1
629629
React-runtimeexecutor: 7285b499d0339104b2813a1f58ad1ada4adbd6c0
630630
ReactCommon: bf2888a826ceedf54b99ad1b6182d1bc4a8a3984
631-
ReactNativePerformanceListsProfiler: 3f9453c24a90c4f77db568d984c48f380968fa0d
631+
ReactNativePerformanceListsProfiler: 7e61653bdfaae62dcec563198df85e2d443c217a
632632
RNFastImage: 1f2cab428712a4baaf78d6169eaec7f622556dd7
633633
RNFlashList: b9b9cad15a69ed4f853315eea89cd73dcc466567
634634
RNGestureHandler: 6e757e487a4834e7280e98e9bac66d2d9c575e9c

ios/Sources/AutoLayoutView.swift

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -80,7 +80,14 @@ import UIKit
8080
layer.animationKeys()?.isEmpty ?? true
8181
else { return }
8282
let cellContainers = subviews
83-
.compactMap { $0 as? CellContainer }
83+
.compactMap { subview -> CellContainer? in
84+
if let cellContainer = subview as? CellContainer {
85+
return cellContainer
86+
} else {
87+
assertionFailure("CellRendererComponent outer view should always be CellContainer. Learn more here: https://shopify.github.io/flash-list/docs/usage#cellrenderercomponent.")
88+
return nil
89+
}
90+
}
8491
.sorted(by: { $0.index < $1.index })
8592
clearGaps(for: cellContainers)
8693
}

src/CellContainer.ts

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,7 @@
11
import { requireNativeComponent } from "react-native";
22

3+
/**
4+
* Behaves as a regular `View` with an extra `index` prop that is saved in the native layer.
5+
*/
36
const CellContainer = requireNativeComponent("CellContainer");
47
export default CellContainer;

src/FlashList.tsx

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,7 @@ import {
1717
import StickyContainer, { StickyContainerProps } from "recyclerlistview/sticky";
1818

1919
import AutoLayoutView from "./AutoLayoutView";
20-
import ItemContainer from "./CellContainer";
20+
import CellContainer from "./CellContainer";
2121
import { PureComponentWrapper } from "./PureComponentWrapper";
2222
import GridLayoutProviderWithProps from "./GridLayoutProviderWithProps";
2323
import CustomError from "./errors/CustomError";
@@ -428,8 +428,10 @@ class FlashList<T> extends React.PureComponent<
428428
};
429429

430430
private itemContainer = (props: any, parentProps: any) => {
431+
const CellRendererComponent =
432+
this.props.CellRendererComponent ?? CellContainer;
431433
return (
432-
<ItemContainer
434+
<CellRendererComponent
433435
{...props}
434436
style={{
435437
...props.style,
@@ -446,7 +448,7 @@ class FlashList<T> extends React.PureComponent<
446448
arg={parentProps.index}
447449
renderer={this.getCellContainerChild}
448450
/>
449-
</ItemContainer>
451+
</CellRendererComponent>
450452
);
451453
};
452454

src/FlashListProps.ts

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -61,6 +61,23 @@ export interface FlashListProps<TItem> extends ScrollViewProps {
6161
*/
6262
estimatedItemSize: number;
6363

64+
/**
65+
* Each cell is rendered using this element.
66+
* Can be a React Component Class, or a render function.
67+
* The root component should always be a `CellContainer` which is also the default component used.
68+
* Ensure that the original `props` are passed to the returned `CellContainer`. The `props` will include the following:
69+
* - `onLayout`: Method for updating data about the real `CellContainer` layout
70+
* - `index`: Index of the cell in the list, you can use this to query data if needed
71+
* - `style`: Style of `CellContainer`, including:
72+
* - `flexDirection`: Depends on whether your list is horizontal or vertical
73+
* - `position`: Value of this will be `absolute` as that's how `FlashList` positions elements
74+
* - `left`: Determines position of the element on x axis
75+
* - `top`: Determines position of the element on y axis
76+
* - `width`: Determines width of the element (present when list is vertical)
77+
* - `height`: Determines height of the element (present when list is horizontal)
78+
*/
79+
CellRendererComponent?: React.ComponentType<any> | undefined;
80+
6481
/**
6582
* Rendered in between each item, but not at the top or bottom. By default, `leadingItem` and `trailingItem` (if available) props are provided.
6683
*/

src/index.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,3 +20,4 @@ export {
2020
export { JSFPSMonitor, JSFPSResult } from "./benchmark/JSFPSMonitor";
2121
export { autoScroll, Cancellable } from "./benchmark/AutoScrollHelper";
2222
export { default as ViewToken } from "./ViewToken";
23+
export { default as CellContainer } from "./CellContainer";

0 commit comments

Comments
 (0)