Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

CellRendererComponent prop #362

Merged
merged 7 commits into from
May 25, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,8 @@ and adheres to [Semantic Versioning](http://semver.org/spec/v2.0.0.html).
- https://github.com/Shopify/flash-list/pull/386
- Change `overrideItemType` prop name to `getItemType`
- https://github.com/Shopify/flash-list/pull/369
- Added `CellRendererComponent` prop
- https://github.com/Shopify/flash-list/pull/362
- Added automatic height measurement for horizontal lists even when parent isn't deterministic
- https://github.com/Shopify/flash-list/pull/409

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -51,7 +51,14 @@ class AutoLayoutView(context: Context) : ReactViewGroup(context) {
* Performance: Sort is needed. Given relatively low number of views in RecyclerListView render tree this should be a non issue.*/
private fun fixLayout() {
if (childCount > 1) {
val positionSortedViews = Array(childCount) { getChildAt(it) as CellContainer }
val positionSortedViews: Array<CellContainer> = Array(childCount) {
val child = getChildAt(it)
if (child is CellContainer) {
child
} else {
throw IllegalStateException("CellRendererComponent outer view should always be CellContainer. Learn more here: https://shopify.github.io/flash-list/docs/usage#cellrenderercomponent.")
}
}
positionSortedViews.sortBy { it.index }
alShadow.offsetFromStart = if (alShadow.horizontal) left else top
alShadow.clearGapsAndOverlaps(positionSortedViews)
Expand Down
34 changes: 33 additions & 1 deletion documentation/docs/fundamentals/usage.md
Original file line number Diff line number Diff line change
Expand Up @@ -93,6 +93,39 @@ estimatedItemSize: number;

---

### `CellRendererComponent`

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:

- `onLayout`: Method for updating data about the real `CellContainer` layout
- `index`: Index of the cell in the list, you can use this to query data if needed
- `style`: Style of `CellContainer`, including:
- `flexDirection`: Depends on whether your list is horizontal or vertical
- `position`: Value of this will be `absolute` as that's how `FlashList` positions elements
- `left`: Determines position of the element on x axis
- `top`: Determines position of the element on y axis
- `width`: Determines width of the element (present when list is vertical)
- `height`: Determines height of the element (present when list is horizontal)
naqvitalha marked this conversation as resolved.
Show resolved Hide resolved

When using with `react-native-reanimated`, you can wrap `CellContainer` in `Animated.createAnimatedComponent` (this is similar to using `Animated.View`):

```ts
const AnimatedCellContainer = Animated.createAnimatedComponent(CellContainer);
return (
<FlashList
CellRendererComponent={(props) => {
return (
<AnimatedCellContainer {...props} style={...}>
);
}}
/>
);
```

```ts
CellRendererComponent?: React.ComponentType<any> | undefined;
```

### `ItemSeparatorComponent`

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

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

- [`CellRendererComponent`](https://reactnative.dev/docs/virtualizedlist#cellrenderercomponent)
- [`columnWrapperStyle`](https://reactnative.dev/docs/flatlist#columnwrapperstyle)
- [`debug`](https://reactnative.dev/docs/virtualizedlist#debug)
- [`listKey`](https://reactnative.dev/docs/virtualizedlist#listkey)
Expand Down
4 changes: 2 additions & 2 deletions fixture/ios/Podfile.lock
Original file line number Diff line number Diff line change
Expand Up @@ -360,7 +360,7 @@ PODS:
- React-jsi (= 0.68.1)
- React-logger (= 0.68.1)
- React-perflogger (= 0.68.1)
- ReactNativePerformanceListsProfiler (0.0.10):
- ReactNativePerformanceListsProfiler (0.0.11):
- React-Core
- RNFastImage (8.5.11):
- React-Core
Expand Down Expand Up @@ -628,7 +628,7 @@ SPEC CHECKSUMS:
React-RCTVibration: 9e344c840176b0af9c84d5019eb4fed8b3c105a1
React-runtimeexecutor: 7285b499d0339104b2813a1f58ad1ada4adbd6c0
ReactCommon: bf2888a826ceedf54b99ad1b6182d1bc4a8a3984
ReactNativePerformanceListsProfiler: 3f9453c24a90c4f77db568d984c48f380968fa0d
ReactNativePerformanceListsProfiler: 7e61653bdfaae62dcec563198df85e2d443c217a
RNFastImage: 1f2cab428712a4baaf78d6169eaec7f622556dd7
RNFlashList: b9b9cad15a69ed4f853315eea89cd73dcc466567
RNGestureHandler: 6e757e487a4834e7280e98e9bac66d2d9c575e9c
Expand Down
9 changes: 8 additions & 1 deletion ios/Sources/AutoLayoutView.swift
Original file line number Diff line number Diff line change
Expand Up @@ -80,7 +80,14 @@ import UIKit
layer.animationKeys()?.isEmpty ?? true
else { return }
let cellContainers = subviews
.compactMap { $0 as? CellContainer }
.compactMap { subview -> CellContainer? in
if let cellContainer = subview as? CellContainer {
return cellContainer
} else {
assertionFailure("CellRendererComponent outer view should always be CellContainer. Learn more here: https://shopify.github.io/flash-list/docs/usage#cellrenderercomponent.")
return nil
}
}
.sorted(by: { $0.index < $1.index })
clearGaps(for: cellContainers)
}
Expand Down
3 changes: 3 additions & 0 deletions src/CellContainer.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,7 @@
import { requireNativeComponent } from "react-native";

/**
* Behaves as a regular `View` with an extra `index` prop that is saved in the native layer.
*/
const CellContainer = requireNativeComponent("CellContainer");
export default CellContainer;
8 changes: 5 additions & 3 deletions src/FlashList.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ import {
import StickyContainer, { StickyContainerProps } from "recyclerlistview/sticky";

import AutoLayoutView from "./AutoLayoutView";
import ItemContainer from "./CellContainer";
import CellContainer from "./CellContainer";
import { PureComponentWrapper } from "./PureComponentWrapper";
import GridLayoutProviderWithProps from "./GridLayoutProviderWithProps";
import CustomError from "./errors/CustomError";
Expand Down Expand Up @@ -428,8 +428,10 @@ class FlashList<T> extends React.PureComponent<
};

private itemContainer = (props: any, parentProps: any) => {
const CellRendererComponent =
this.props.CellRendererComponent ?? CellContainer;
return (
<ItemContainer
<CellRendererComponent
{...props}
style={{
...props.style,
Expand All @@ -446,7 +448,7 @@ class FlashList<T> extends React.PureComponent<
arg={parentProps.index}
renderer={this.getCellContainerChild}
/>
</ItemContainer>
</CellRendererComponent>
);
};

Expand Down
17 changes: 17 additions & 0 deletions src/FlashListProps.ts
Original file line number Diff line number Diff line change
Expand Up @@ -61,6 +61,23 @@ export interface FlashListProps<TItem> extends ScrollViewProps {
*/
estimatedItemSize: number;

/**
* 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` will include the following:
* - `onLayout`: Method for updating data about the real `CellContainer` layout
* - `index`: Index of the cell in the list, you can use this to query data if needed
* - `style`: Style of `CellContainer`, including:
* - `flexDirection`: Depends on whether your list is horizontal or vertical
* - `position`: Value of this will be `absolute` as that's how `FlashList` positions elements
* - `left`: Determines position of the element on x axis
* - `top`: Determines position of the element on y axis
* - `width`: Determines width of the element (present when list is vertical)
* - `height`: Determines height of the element (present when list is horizontal)
*/
CellRendererComponent?: React.ComponentType<any> | undefined;

/**
* Rendered in between each item, but not at the top or bottom. By default, `leadingItem` and `trailingItem` (if available) props are provided.
*/
Expand Down
1 change: 1 addition & 0 deletions src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -20,3 +20,4 @@ export {
export { JSFPSMonitor, JSFPSResult } from "./benchmark/JSFPSMonitor";
export { autoScroll, Cancellable } from "./benchmark/AutoScrollHelper";
export { default as ViewToken } from "./ViewToken";
export { default as CellContainer } from "./CellContainer";