mcpField
O
onboardjs-react

onboardjs-react

github.com/onboardjs开发385 次安装39 周前更新
代码仓库

关于 onboardjs-react

onboardjs-react 是使用 OnboardJS 构建引导流程的 React 集成指南,面向需要自定义界面并由库处理流程状态与导航的开发者。

主要功能

  1. 01React 项目与包管理器检测
  2. 02步骤组件和 Provider 配置
  3. 03用于渲染与导航的引导 Hook
  4. 04通过步骤 ID 跟踪进度
  5. 05Next.js 客户端设置与延迟加载
  6. 06本地及自定义后端持久化

适用场景

  1. 01为 React 应用添加引导流程
  2. 02将引导设置适配到 Next.js
  3. 03跟踪引导步骤进度
  4. 04通过本地存储或 API 保存引导状态

安装

复制下面的命令,在终端中运行。

$npx skills add https://github.com/onboardjs/onboardjs-skills --skill onboardjs-react

安装后在 Claude Code 中输入/onboardjs-react确认已加载。

包含的文件

除 SKILL.md 外,安装时一起下载的参考资料和脚本。

references/
hooks-api.md
patterns.md
step-config.md

详细介绍

OnboardJS React 集成

OnboardJS 是一个用于构建用户引导体验的无界面库。你可以控制 UI;OnboardJS 负责处理流程逻辑、状态、持久化和导航。

开始之前

1. 确认 React 项目 - 检查是否存在包含 React 依赖项的 package.json。如果不是 React 项目,请告知用户。

2. 检测包管理器 - 检查锁定文件,以确定正确的安装命令:

锁定文件包管理器安装命令
pnpm-lock.yamlpnpmpnpm add @onboardjs/core @onboardjs/react
yarn.lockyarnyarn add @onboardjs/core @onboardjs/react
bun.lockbbunbun add @onboardjs/core @onboardjs/react
package-lock.json 或无锁定文件npmnpm install @onboardjs/core @onboardjs/react

3. 检测 Next.js - 检查是否存在 next.config.js、next.config.mjs,或依赖项中是否包含 next。如果是 Next.js 项目,请参阅Next.js 设置部分。

安装

# npm
npm install @onboardjs/core @onboardjs/react

# pnpm
pnpm add @onboardjs/core @onboardjs/react

# yarn
yarn add @onboardjs/core @onboardjs/react

# bun
bun add @onboardjs/core @onboardjs/react

快速设置

1. 使用组件定义步骤
// steps.tsx
import { OnboardingStep } from '@onboardjs/react'
import { WelcomeStep } from './components/WelcomeStep'
import { ProfileFormStep } from './components/ProfileFormStep'
import { CompleteStep } from './components/CompleteStep'

const steps: OnboardingStep[] = [
  {
    id: 'welcome',
    component: WelcomeStep,
    payload: { title: 'Welcome!', description: 'Let\'s get started' },
    nextStep: 'profile'
  },
  {
    id: 'profile',
    component: ProfileFormStep,
    nextStep: 'complete'
  },
  {
    id: 'complete',
    component: CompleteStep,
    payload: { title: 'All done!' },
    nextStep: null
  }
]
2. 使用 Provider 包装
import { OnboardingProvider } from '@onboardjs/react'
import { steps } from './steps'

function App() {
  return (
    <OnboardingProvider
      steps={steps}
      onFlowComplete={(ctx) => console.log('Done!', ctx)}
    >
      <OnboardingUI />
    </OnboardingProvider>
  )
}
3. 使用 Hook
import { useOnboarding } from '@onboardjs/react'

function OnboardingUI() {
  const { renderStep, next, previous, state, loading } = useOnboarding()

  if (loading.isHydrating) return <Spinner />
  if (state?.isCompleted) return <CompletedScreen />

  return (
    <div>
      {renderStep()}
      <div>
        <button onClick={previous} disabled={state?.isFirstStep}>Back</button>
        <button onClick={() => next()} disabled={!state?.canGoNext}>
          {state?.isLastStep ? 'Finish' : 'Next'}
        </button>
      </div>
    </div>
  )
}

步骤指示器 / 进度

重要: state 对象没有 steps 数组。请使用 currentStepNumber 和 totalSteps,或根据步骤 ID 进行计算。

选项 1:使用内置属性(如果可用)
function OnboardingUI() {
  const { state, renderStep } = useOnboarding()

  return (
    <div>
      {state?.currentStepNumber && state?.totalSteps && (
        <div>Step {state.currentStepNumber} of {state.totalSteps}</div>
      )}
      <progress
        value={state?.currentStepNumber ?? 0}
        max={state?.totalSteps ?? 1}
      />
      {renderStep()}
    </div>
  )
}
选项 2:根据步骤 ID 计算(推荐)

若要可靠地跟踪进度,请定义一次步骤 ID 并计算索引:

// steps.tsx - export your step IDs
export const STEP_IDS = ['welcome', 'profile', 'preferences', 'complete'] as const

export const steps: OnboardingStep[] = [
  { id: 'welcome', component: WelcomeStep, nextStep: 'profile' },
  { id: 'profile', component: ProfileStep, nextStep: 'preferences' },
  { id: 'preferences', component: PreferencesStep, nextStep: 'complete' },
  { id: 'complete', component: CompleteStep, nextStep: null }
]
// OnboardingUI.tsx
import { STEP_IDS } from './steps'

function OnboardingUI() {
  const { state, renderStep } = useOnboarding()

  const currentIndex = STEP_IDS.findIndex(id => id === state?.currentStep?.id)
  const currentStepNumber = currentIndex + 1
  const totalSteps = STEP_IDS.length

  return (
    <div>
      <div>Step {currentStepNumber} of {totalSteps}</div>
      <progress value={currentStepNumber} max={totalSteps} />
      {renderStep()}
    </div>
  )
}
步骤指示器组件
interface StepIndicatorProps {
  stepIds: readonly string[]
  currentStepId: string | undefined
}

function StepIndicator({ stepIds, currentStepId }: StepIndicatorProps) {
  const currentIndex = stepIds.findIndex(id => id === currentStepId)

  return (
    <div className="flex gap-2">
      {stepIds.map((id, index) => (
        <div
          key={id}
          className={`w-3 h-3 rounded-full ${
            index < currentIndex ? 'bg-green-500' :
            index === currentIndex ? 'bg-blue-500' :
            'bg-gray-300'
          }`}
        />
      ))}
    </div>
  )
}

// Usage
<StepIndicator stepIds={STEP_IDS} currentStepId={state?.currentStep?.id} />

步骤组件模式

import { StepComponentProps } from '@onboardjs/react'

interface ProfilePayload {
  title: string
  fields: string[]
}

const ProfileFormStep: React.FC<StepComponentProps<ProfilePayload>> = ({
  payload,
  context,
  onDataChange,
  initialData
}) => {
  const [name, setName] = useState(initialData?.name || '')

  const handleChange = (value: string) => {
    setName(value)
    onDataChange?.({ name: value }, value.length > 0)
  }

  return (
    <div>
      <h2>{payload.title}</h2>
      <input value={name} onChange={(e) => handleChange(e.target.value)} />
    </div>
  )
}

Next.js 设置

OnboardJS 使用 React Hooks 和浏览器 API,因此在 Next.js App Router 中需要客户端渲染。

步骤组件 - 添加 "use client"

所有步骤组件都必须是客户端组件:

// components/WelcomeStep.tsx
'use client'

import { StepComponentProps } from '@onboardjs/react'

export const WelcomeStep: React.FC<StepComponentProps> = ({ payload }) => {
  return <h1>{payload.title}</h1>
}
Provider 包装器 - 添加 "use client"

为 Provider 创建一个客户端包装器:

// components/OnboardingWrapper.tsx
'use client'

import { OnboardingProvider } from '@onboardjs/react'
import { steps } from './steps'

export function OnboardingWrapper({ children }: { children: React.ReactNode }) {
  return (
    <OnboardingProvider
      steps={steps}
      onFlowComplete={(ctx) => console.log('Done!', ctx)}
    >
      {children}
    </OnboardingProvider>
  )
}
在页面中使用(App Router)
// app/onboarding/page.tsx
import { OnboardingWrapper } from '@/components/OnboardingWrapper'
import { OnboardingUI } from '@/components/OnboardingUI'

export default function OnboardingPage() {
  return (
    <OnboardingWrapper>
      <OnboardingUI />
    </OnboardingWrapper>
  )
}
动态导入(可选 - 用于代码拆分)

使用动态导入以减小初始包体积:

// app/onboarding/page.tsx
import dynamic from 'next/dynamic'

const OnboardingWrapper = dynamic(
  () => import('@/components/OnboardingWrapper').then(mod => mod.OnboardingWrapper),
  {
    ssr: false,
    loading: () => <div>Loading onboarding...</div>
  }
)

export default function OnboardingPage() {
  return <OnboardingWrapper><OnboardingUI /></OnboardingWrapper>
}
动态步骤组件(可选 - 用于大型流程)

延迟加载步骤组件以减小包体积:

// steps.tsx
'use client'

import dynamic from 'next/dynamic'
import { OnboardingStep } from '@onboardjs/react'

const WelcomeStep = dynamic(() => import('./components/WelcomeStep').then(m => m.WelcomeStep))
const ProfileStep = dynamic(() => import('./components/ProfileStep').then(m => m.ProfileStep))
const CompleteStep = dynamic(() => import('./components/CompleteStep').then(m => m.CompleteStep))

export const steps: OnboardingStep[] = [
  { id: 'welcome', component: WelcomeStep, nextStep: 'profile' },
  { id: 'profile', component: ProfileStep, nextStep: 'complete' },
  { id: 'complete', component: CompleteStep, nextStep: null }
]
Pages Router(旧版)

对于 Next.js Pages Router,无需使用 "use client",但要禁用 SSR:

// pages/onboarding.tsx
import dynamic from 'next/dynamic'

const OnboardingFlow = dynamic(
  () => import('@/components/OnboardingFlow'),
  { ssr: false }
)

export default function OnboardingPage() {
  return <OnboardingFlow />
}

持久化

localStorage(简单方式)
<OnboardingProvider
  steps={steps}
  localStoragePersistence={{ key: 'onboarding_v1', ttl: 604800000 }}
>
自定义后端
<OnboardingProvider
  steps={steps}
  customOnDataLoad={async () => await fetchFromAPI()}
  customOnDataPersist={async (ctx) => await saveToAPI(ctx)}
  customOnClearPersistedData={async () => await clearAPI()}
>

条件导航

import { RoleSelectStep } from './components/RoleSelectStep'
import { AdminSetupStep } from './components/AdminSetupStep'
import { UserSetupStep } from './components/UserSetupStep'

{
  id: 'role-select',
  component: RoleSelectStep,
  payload: {
    options: [
      { value: 'admin', label: 'Admin' },
      { value: 'user', label: 'User' }
    ]
  },
  nextStep: (ctx) => ctx.flowData.role === 'admin' ? 'admin-setup' : 'user-setup'
}

条件步骤可见性

{
  id: 'admin-setup',
  component: AdminSetupStep,
  condition: (ctx) => ctx.flowData.role === 'admin'
}

进阶:参阅参考资料

评论

支持 Markdown