diff --git a/.changeset/nice-pans-confess.md b/.changeset/nice-pans-confess.md
new file mode 100644
index 0000000000..60afe186a2
--- /dev/null
+++ b/.changeset/nice-pans-confess.md
@@ -0,0 +1,5 @@
+---
+'@td-design/react-native-tabs': minor
+---
+
+feat: 为Tabs增加lazy功能
diff --git a/packages/react-native-tabs/src/SceneView.tsx b/packages/react-native-tabs/src/SceneView.tsx
new file mode 100644
index 0000000000..6dba4d946a
--- /dev/null
+++ b/packages/react-native-tabs/src/SceneView.tsx
@@ -0,0 +1,51 @@
+import React, { useEffect } from 'react';
+import { View } from 'react-native';
+
+import { useSafeState } from '@td-design/rn-hooks';
+
+export default function SceneView({
+ lazy,
+ currentPage,
+ index,
+ children,
+}: {
+ lazy: boolean;
+ currentPage: number;
+ index: number;
+ children: (props: { loading: boolean }) => React.ReactNode;
+}) {
+ const [isLoading, setIsLoading] = useSafeState(lazy && Math.abs(currentPage - index) > 0);
+
+ if (isLoading && Math.abs(currentPage - index) <= 0 && lazy) {
+ setIsLoading(false);
+ }
+
+ useEffect(() => {
+ let timer: NodeJS.Timeout | undefined;
+
+ if (!lazy && isLoading) {
+ timer = setTimeout(() => {
+ setIsLoading(false);
+ }, 0);
+ }
+
+ return () => {
+ clearTimeout(timer);
+ };
+ }, [lazy, isLoading]);
+
+ const focused = currentPage === index;
+
+ return (
+
+ {children({ loading: isLoading })}
+
+ );
+}
diff --git a/packages/react-native-tabs/src/index.md b/packages/react-native-tabs/src/index.md
index 78109419fb..68be5104ea 100644
--- a/packages/react-native-tabs/src/index.md
+++ b/packages/react-native-tabs/src/index.md
@@ -197,6 +197,8 @@ return (
| tabItemStyle | `false` | 选项卡标签样式 | `ViewStyle` | |
| labelStyle | `false` | 标签文字样式 | `TextStyle` | |
| indicatorStyle | `false` | 指示器样式 | `ViewStyle` | |
+| lazy | `false` | 是否启用懒加载模式 | `boolean` | |
+| lazyPlaceholder | `false` | 懒加载时的placeholder组件 | `() => ReactNode` | |
```ts
interface TabScene {
diff --git a/packages/react-native-tabs/src/index.tsx b/packages/react-native-tabs/src/index.tsx
index 01454fa727..96a52602f1 100644
--- a/packages/react-native-tabs/src/index.tsx
+++ b/packages/react-native-tabs/src/index.tsx
@@ -1,9 +1,10 @@
-import React, { cloneElement } from 'react';
+import { ComponentType, createElement, memo } from 'react';
import { Animated, StyleProp, TextStyle, ViewStyle } from 'react-native';
import PagerView from 'react-native-pager-view';
import { Box, helpers } from '@td-design/react-native';
+import SceneView from './SceneView';
import ScrollBar from './ScrollBar';
import TabBar from './TabBar';
import usePagerView from './usePagerView';
@@ -13,11 +14,13 @@ const AnimatedPagerView = Animated.createAnimatedComponent(Pag
type Tab = {
title: string;
- component: JSX.Element;
+ component: ComponentType;
};
export interface TabsProps {
scenes: Tab[];
+ lazy?: boolean;
+ renderLazyPlaceholder?: () => JSX.Element;
/** 默认当前是第几个tab */
initialPage?: number;
/** 当前是第几个tab */
@@ -42,6 +45,8 @@ export interface TabsProps {
export default function Tabs({
initialPage = 0,
+ lazy = false,
+ renderLazyPlaceholder,
page,
onChange,
scenes = [],
@@ -103,8 +108,20 @@ export default function Tabs({
onPageSelected={onPageSelected}
onPageScrollStateChanged={onPageScrollStateChanged}
>
- {scenes.map(({ title, component }) => cloneElement(component, { key: title }))}
+ {scenes.map(({ component }, i) => (
+
+ {({ loading }) => {
+ if (loading) return renderLazyPlaceholder?.();
+
+ return ;
+ }}
+
+ ))}
);
}
+
+const SceneComponent = memo( }>({ component }: T) => {
+ return createElement(component);
+});
diff --git a/packages/react-native-tabs/src/types.tsx b/packages/react-native-tabs/src/types.tsx
new file mode 100644
index 0000000000..ccedee3489
--- /dev/null
+++ b/packages/react-native-tabs/src/types.tsx
@@ -0,0 +1,14 @@
+export type Route = {
+ key: string;
+ icon?: string;
+ title?: string;
+};
+
+export type Scene = {
+ route: T;
+};
+
+export type NavigationState = {
+ index: number;
+ routes: T[];
+};