{"id":26488046,"url":"https://github.com/itenl/react-native-scrollable-tabview","last_synced_at":"2025-04-09T18:23:18.930Z","repository":{"id":38369510,"uuid":"373123430","full_name":"itenl/react-native-scrollable-tabview","owner":"itenl","description":"Based on pure JS scripts, without relying on native, no need for react-native link, Title / Header / Tabs / Sticky / Screen components can be flexibly configured, among which Tabs / Sticky can slide When it reaches the top, it will be topped.","archived":false,"fork":false,"pushed_at":"2022-05-30T03:44:36.000Z","size":11393,"stargazers_count":155,"open_issues_count":2,"forks_count":14,"subscribers_count":2,"default_branch":"main","last_synced_at":"2025-04-02T17:06:56.224Z","etag":null,"topics":["react","react-native","scrollable","sticky","tabbar","tabview","tiktok"],"latest_commit_sha":null,"homepage":"","language":"JavaScript","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/itenl.png","metadata":{"files":{"readme":"README-zh_CN.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null}},"created_at":"2021-06-02T10:16:29.000Z","updated_at":"2025-02-27T22:39:41.000Z","dependencies_parsed_at":"2022-08-23T19:50:53.933Z","dependency_job_id":null,"html_url":"https://github.com/itenl/react-native-scrollable-tabview","commit_stats":null,"previous_names":[],"tags_count":8,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/itenl%2Freact-native-scrollable-tabview","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/itenl%2Freact-native-scrollable-tabview/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/itenl%2Freact-native-scrollable-tabview/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/itenl%2Freact-native-scrollable-tabview/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/itenl","download_url":"https://codeload.github.com/itenl/react-native-scrollable-tabview/tar.gz/refs/heads/main","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":248086000,"owners_count":21045250,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["react","react-native","scrollable","sticky","tabbar","tabview","tiktok"],"created_at":"2025-03-20T06:55:44.698Z","updated_at":"2025-04-09T18:23:18.913Z","avatar_url":"https://github.com/itenl.png","language":"JavaScript","funding_links":[],"categories":[],"sub_categories":[],"readme":"# react-native-scrollable-tabview\n\n[![NPM Version](http://img.shields.io/npm/v/@itenl/react-native-scrollable-tabview.svg?style=flat)](https://www.npmjs.com/package/@itenl/react-native-scrollable-tabview)\n\n[English](./README.md) | 简体中文\n\n基于纯 `JS` 脚本，不依赖原生，无需 `react-native link`，`Title` / `Header` / `Tabs` / `Sticky` / `Screen` 组件可灵活配置，其中 `Tabs` / `Sticky` 可在滑动到顶部时会进行吸顶；我们所支持的是以栈的形式独立管理自身的 `Sticky` / `Screen` / `Badge` / `tabLabel` 各项配置，并且为 `Screen` 注入[生命周期](#InjectionLifecycle) `onRefresh` / `onEndReached`它们将在下拉刷新与滚动条触底时触发，最后还为 `Screen` / `Sticky` 注入了更多 [props](#InjectionScreenProps)\n\n##### Table of Contents\n* [Example-API](https://github.com/itenl/react-native-scrollable-tabview-example-app)\n* [Example-TikTok](https://github.com/itenl/react-native-scrollable-tabview-example-tiktok)\n* [Features](#features)\n* [Installation](#installation)\n* [Usage](#usage)\n* [Props](#props)\n* [Method](#method)\n* [Stack Property](#StackProperty)\n* [Badge Property](#BadgeProperty)\n* [Injection lifecycle to Screen](#InjectionLifecycle)\n* [Injection props to Screen](#InjectionScreenProps)\n* [Injection props to Sticky](#InjectionStickyProps)\n* [Known Issues](#KnownIssues)\n* [Snapshot](#Snapshot)\n\n## \u003ca name=\"features\"/\u003eFeatures\n* 支持为每个 `Screen` 设置下拉刷新与上滑加载更多(生命周期注入或属性注入)\n* Tabs 支持固定自适应与水平滚动两种配置方式\n* 允许为每个 `Screen` 独立配置 `Sticky` 组件\n* 允许为每个 `Tab` 独立配置自定义的徽章\n* 支持下拉刷新与上滑加载更多前置函数 `onBeforeRefresh` / `onBeforeEndReached` \n* 支持动画标题，可支持动画为 `interpolate.opacity` 与 `interpolate.height`\n\n## \u003ca name=\"installation\"/\u003eInstallation\n\n```shell\nnpm i @itenl/react-native-scrollable-tabview\n```\nor\n```shell\nyarn add @itenl/react-native-scrollable-tabview\n```\n\n## \u003ca name=\"usage\"/\u003eUsage\n\n```jsx\nimport React from 'react';\nimport ScrollableTabView from '@itenl/react-native-scrollable-tabview';\n\nfunction App() {\n  return (\n      \u003cScrollableTabView\n        ref={rf =\u003e (this.scrollableTabView = rf)}\n        mappingProps={{\n          fromRootEst: this.state.est,\n        }}\n        badges={[\n          null,\n          [\n            \u003cView\n              style={{\n                position: 'absolute',\n                zIndex: 100,\n                top: 10,\n                right: 0,\n              }}\n            \u003e\n              \u003cText\u003enew\u003c/Text\u003e\n            \u003c/View\u003e,\n            \u003cView\n              style={{\n                position: 'absolute',\n                width: 150,\n                height: 50,\n                zIndex: 100,\n                marginTop: 35,\n                right: 0,\n                opacity: 0.6,\n                backgroundColor: 'pink',\n                justifyContent: 'center',\n                alignItems: 'center',\n              }}\n            \u003e\n              \u003cText\u003eThree Tips\u003c/Text\u003e\n            \u003c/View\u003e,\n          ],\n        ]}\n        stacks={[\n          {\n            screen: One,\n            sticky: Sticky,\n            tabLabel: 'OneTab',\n            tabLabelRender: tabLabel =\u003e {\n              return `--- ${tabLabel} ---`;\n            },\n            badge: [\u003cText\u003eone\u003c/Text\u003e, \u003cText\u003etwo\u003c/Text\u003e],\n            toProps: {\n              xx: 123,\n            },\n          }, {\n            screen: ({\n              layoutHeight,\n              refresh,\n              scrollTo,\n              toTabView,\n              initScreen,\n              onRefresh,\n              onEndReached,\n            }) =\u003e {\n              // The code is required\n              initScreen();\n              const [datetime, setDatetime] = useState(Date.now());\n              useEffect(() =\u003e {\n                setInterval(() =\u003e {\n                  setDatetime(Date.now());\n                }, 1000);\n              }, []);\n              onRefresh((toggled) =\u003e {\n                toggled(true);\n                alert(\"onRefresh start\");\n                setTimeout(() =\u003e {\n                  toggled(false);\n                  alert(\"onRefresh stop\");\n                }, 3000);\n              });\n              onEndReached(() =\u003e {\n                alert(\"onEndReached\");\n              });\n              return (\n                \u003cView\n                  style={{\n                    flex: 1,\n                    backgroundColor: \"#151723\",\n                    justifyContent: \"center\",\n                    alignItems: \"center\",\n                  }}\n                \u003e\n                  \u003cText style={{ color: \"#ffffff\" }}\u003e\n                    Test function component {datetime}\n                  \u003c/Text\u003e\n                \u003c/View\u003e\n              );\n            },\n            tabLabel: \"TestFunctionComponent\",\n          }\n        ]}\n        tabsStyle={{}}\n        tabWrapStyle={{}}\n        tabInnerStyle={{}}\n        tabActiveOpacity={0.6}\n        tabStyle={{}}\n        textStyle={{}}\n        textActiveStyle={{}}\n        tabUnderlineStyle={{}}\n        firstIndex={0}\n        syncToSticky={true}\n        onEndReachedThreshold={0.1}\n        onBeforeRefresh={(next, toggled) =\u003e {\n          toggled();\n          next();\n        }}\n        onBeforeEndReached={next =\u003e {\n          next();\n        }}\n        onTabviewChanged={(index, tabLabel, isFirst) =\u003e {\n          alert(index);\n        }}\n        header={() =\u003e {\n          return \u003cView style={{ backgroundColor: 'pink', height: 120 }}\u003e\u003c/View\u003e;\n        }}\n        oneTabHidden={true}\n        enableCachePage={true}\n        carouselProps={{}}\n        sectionListProps={{}}\n        toHeaderOnTab={true}\n        toTabsOnTab={true}\n        tabsShown={false}\n        fixedTabs={false}\n        fixedHeader={false}\n        useScroll={false}\n        fillScreen={true}\n      \u003e\u003c/ScrollableTabView\u003e\n  );\n}\n```\n\n## \u003ca name=\"props\"/\u003eProps\n\nAll props are optional\n\nProp              | Type     | Default     | Description\n----------------- | -------- | ----------- | -----------\n**`stacks`**            | Array    | []          | 页面栈 \u003c [阅读 Stack Property](#StackProperty) \u003e\n**`mappingProps`**      | Object   | {}          | 关联映射数据到 Stack / Sticky\n**`badges`**             | Array    | []          | 针对每个Tab的徽章 \u003c [阅读 Badge Property](#BadgeProperty) \u003e\n**`tabsStyle`**             | Object    | {}          | 整个Tabs样式\n**`tabWrapStyle`**             | Object / Function    | {}          | 单个Tab外包装样式 (函数参数提供了item, index, 需要返回样式对象，eg. **`return index == 1 \u0026\u0026 { zIndex : 10}`**)\n**`tabInnerStyle`**             | Object    | {}          | 单个Tab内包装样式\n**`tabActiveOpacity`**             | Number    | 0.6          | Tab按钮点击后透明度\n**`tabStyle`**             | Object    | {}          | 单个Tab样式\n**`textStyle`**             | Object    | {}          | Tab内文本样式\n**`textActiveStyle`**             | Object    | {}          | 选中激活的text样式\n**`tabUnderlineStyle`**             | Object    | {}          | 选中激活的下划线样式\n**`firstIndex`**             | Number / Null    | null          | 设置 **`firstIndex`** 的栈为活动状态 (请在设定 **`firstIndex`** 值的时候确保 **`stacks`** 的个数大于 **`firstIndex`** )\n**`syncToSticky`**             | Boolean    | true          | 是否同步(Screen中发生 **`render`** 触发 **`componentDidUpdate`** 将更新Sticky)\n**`onEndReachedThreshold`**             | Number    | 0.2          | 触底回调阈值\n**`onBeforeRefresh`**             | Function    | null          | 下拉刷新前置函数, 执行 **`next`** 将执行Screen中 **`onRefresh`** 函数，执行 **`toggled`** 将切换系统loading,可传 true / false 进行指定 (回调含有 **`next`** , **`toggled`** 两个形参)\n**`onBeforeEndReached`**             | Function    | null          | 上滑加载更多前置函数, 执行next将执行Screen中 **`onEndReached`** 函数 (回调含有 **`next`** 形参)\n**`onTabviewChanged`**             | Function    | null          | Tab切换完成回调 (回调含有 **`index`**, **`tabLabel`**, **`isFirst`** 形参)\n**`screenScrollThrottle`**             | Number    | 60          | **`Screen`** 横向滑动时节流参数,单位 (毫秒)\n**`header`**             | Function / JSX Element / Class Component    | null          | 顶部组件 (若是函数需要返回 Element)\n**`stickyHeader`**             | Function / JSX Element / Class Component    | null          | 顶部带吸顶效果组件 (若是函数需要返回 Element)\n**`oneTabHidden`**             | Boolean    | false          | 仅一个Tab时将隐藏自身\n**`enableCachePage`**             | Boolean    | true          | 是否持久化页面切换后不销毁\n**`carouselProps`**             | Object    | {}          | 传递给 Carousel 的剩余属性 \u003c [阅读 Carousel](https://github.com/meliorence/react-native-snap-carousel/blob/master/doc/PROPS_METHODS_AND_GETTERS.md) \u003e\n**`sectionListProps`**             | Object    | {}          | 传递给 SectionList 的剩余属性 \u003c [阅读 SectionList](https://reactnative.dev/docs/sectionlist) \u003e\n**`toHeaderOnTab`**             | Boolean    | false          | 点击触发已激活的Tab将回到Header(高优先级)\n**`toTabsOnTab`**             | Boolean    | false          | 点击触发已激活的Tab将回到Tabs\n**`tabsShown`**             | Boolean    | true          | 配置 Tabs 显示隐藏\n**`fixedTabs`**             | Boolean    | false          | 在 **`enableCachePage`** 为true的情况下滑动切换Screen设置最小高度保障Header与Tabs不会弹跳\n**`fixedHeader`**             | Boolean    | false          | 与Tabs一同渲染，固定顶部Header，不跟随滚动\n**`useScroll`**             | Boolean    | false          | Tabs是否支持横向滚动(存在多个类目Tab的情况需要启用，建议 **`tabStyle`** 传入固定宽度)\n**`useScrollStyle`**             | Object    | {}          | 为滚动的 **`Tabs`** 设置 **`contentContainerStyle`**，常见为左右两侧添加边距 **`paddingLeft`** **`paddingHorizontal`**\n**`fillScreen`**             | Boolean    | true          | 填充整个 Screen\n**`title`**             | Function / JSX Element / Class Component    | null          | 动画标题\n**`titleArgs`**             | Object    | **`{  style: {}, interpolateOpacity: {}, interpolateHeight: {} }`**          | 标题配置 \u003c [阅读 interpolate](https://reactnative.dev/docs/animations#interpolation) \u003e\n**`onScroll`**             | Function    | null          | 滚动事件监听\n**`onScroll2Horizontal`**             | Function    | null          | 滚动事件监听(横向)\n**`tabsEnableAnimated`**             | Boolean    | false          | 为Tabs启用滑动效果，需要为 **`tabStyle`** 指定 **`width`**\n**`tabsEnableAnimatedUnderlineWidth`**             | Number    | 0          | 为Tabs Underline设定固定宽度并添加弹跳动画，需要启用 **`tabsEnableAnimated=true`**.( 建议传入 **`tabStyle.width`** 的三分之一或固定 30px )\n**`errorToThrow`**             | Boolean    | false          | **`console.error`** 将会抛出错误 **`throw new Error()`**\n\n\n## \u003ca name=\"method\"/\u003eMethod\n\n``` javascript\n  \u003cScrollableTabView\n    ref={rf =\u003e (this.scrollableTabView = rf)}\n  \u003e\n  \u003c/ScrollableTabView\u003e \n  this.scrollableTabView.getCurrentRef();\n  this.scrollableTabView.toTabView(1);\n  this.scrollableTabView.scrollTo(0);\n  this.scrollableTabView.clearStacks(()=\u003ealert('done'));\n  \n```\n\nName              | Type     | Description\n----------------- | -------- | -----------\n**`getCurrentRef(index: number.optional)`**            | Function   | 获取当前活动的视图的实例，可传 **`index`** 获取指定实例\n**`toTabView(index: number.required / label: string.required)`**            | Function   | 跳到指定 Screen\n**`scrollTo(index: number.required)`**            | Function   | 上下滑动至指定位置 (传入 0 默认定位至 tabs / 传入负数则置顶)\n**`clearStacks(callback: function.optional)`**            | Function   | 清空栈以及相关状态 (Tabs / Badge / Stacks))\n\n## \u003ca name=\"StackProperty\"/\u003eStack Property\n\nName              | Type     | Description\n----------------- | -------- | -----------\n**`screen`**            | Class Component   | Screen 类组件\n**`sticky`**            | Class Component   | 吸顶类组件, 实例内将返回该类组件的上下文\n**`tabLabel`**            | String   | Tab 昵称\n**`tabLabelRender`**            | Function   | 自定义 Tab渲染函数，优先级高于 **`tabLabel`**\n**`badge`**            | Array    | 针对当前 Tab 的徽章，与 **`badges`** 属性互斥，优先级高于最外层属性 **`badges`** \u003c [阅读 Badge Property](#BadgeProperty) \u003e\n**`toProps`**            | Object    | **`toProps`** 仅传递给 Screen，不作数据关联\n\n## \u003ca name=\"BadgeProperty\"/\u003eBadge Property\n\nType     | Description\n-------- | -----------\nJSX Element   | 基于当前Tab进行渲染的 徽章 / 悬浮 Tips 等\n\n\n##  \u003ca name=\"InjectionLifecycle\"/\u003eInjection lifecycle to Screen(仅在类组件)\n\nName              | Type     | Description\n----------------- | -------- | -----------\n**`onRefresh`**            | Function   | 下拉刷新时触发,形参 **`toggled`** 函数用于切换原生 loading 状态的显隐，若在 loading 中用户切换 tab 将会强制隐藏并重置状态\n**`onEndReached`**            | Function   | 上滑加载更多触发\n\n\n##  \u003ca name=\"InjectionScreenProps\"/\u003eInjection props to Screen\n\nName              | Type     | Description\n----------------- | -------- | -----------\n**`refresh()`**            | Function   | 手动触发刷新、同步Screen状态至Sticky\n**`scrollTo(index: number.required)`**            | Function   | 上下滑动至指定位置 (传入 0 默认定位至 tabs / 传入负数则置顶)\n**`toTabView(index: number.required / label: string.required)`**            | Function   | 跳到指定 Screen\n**`layoutHeight.container`**            | Number   | Container 容器总高度\n**`layoutHeight.header`**            | Number   | Header 高度\n**`layoutHeight.tabs`**            | Number   | Tabs 高度\n**`layoutHeight.screen`**            | Number   | 视图 高度\n**`initScreen`**            | Function   | (仅在函数组件) 如果是函数组件则必须调用\n**`onRefresh`**            | Function   | (仅在函数组件) \u003c [阅读 onRefresh 描述](#InjectionLifecycle) \u003e\n**`onEndReached`**            | Function   | (仅在函数组件) \u003c [阅读 onEndReached 描述](#InjectionLifecycle) \u003e\n\n##  \u003ca name=\"InjectionStickyProps\"/\u003eInjection props to Sticky\n\nName              | Type     | Description\n----------------- | -------- | -----------\n**`screenContext`**            | Object   | 获取 Screen 上下文\n\n## \u003ca name=\"KnownIssues\"/\u003eKnown Issues\n- 如果你仅仅是新增 `Stack` 可以 `Push` 即可，但如果需要更新或者删除 `Stack` 请使用 [clearStacks](#Method) 后在添加你所需要的 `Stacks`\n\n## \u003ca name=\"Snapshot\"/\u003eSnapshot\n\n### Android (Sliding Tabs)\n\u003cimg src=\"./snapshot/e18k6-3jmxk.gif\" /\u003e\n\n### iOS (Bounce Tabs)\n\u003cimg src=\"./snapshot/e18k6-3jmxk-2.gif\" /\u003e\n\n### API Example\n\u003cimg src=\"./snapshot/qoz8r-klpuc.gif\" /\u003e\n\u003cbr /\u003e\n\n---\n\n**MIT Licensed**","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fitenl%2Freact-native-scrollable-tabview","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fitenl%2Freact-native-scrollable-tabview","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fitenl%2Freact-native-scrollable-tabview/lists"}