react-okr-tree Logoreact-okr-tree
开始

安装与快速开始

npm / pnpm / CDN UMD 三种引入路径、样式引入、最小可用示例、React 版本要求。

环境要求

依赖要求说明
react / react-dom>= 18.2.0(peer)用到的能力(useSyncExternalStore / forwardRef / useImperativeHandle / Context)自 18.2 起齐备;CI 在 18.2 与 19.2 两档 peer 矩阵下各跑一遍全量单测
TypeScript可选产物带单文件 index.d.tsimport)与 index.d.ctsrequire);不写 TS 也能用全部 API
html-to-image^1.11.0,可选 peer只有 OkrTreeViewportexportImage() 需要;未安装时调用会抛带安装指引的错。它不在 dependencies 里,不需要导出能力就不必装

安装

pnpm add react-okr-tree

引入样式

组件不自动注入样式,需要显式引一次(通常在应用入口):

import 'react-okr-tree/style.css'

react-okr-tree/style.cssreact-okr-tree/dist/style.css 是同一个文件的两个导出路径,任选其一。漏掉这一步不会报错,但页面上只剩无外观的纯结构——连接线、卡片、展开按钮全在这份 CSS 里。

最小可用示例

'use client'

import { useMemo } from 'react'
import { OkrTree, type TreeNodeData } from 'react-okr-tree'
import 'react-okr-tree/style.css'

export default function App() {
  const data = useMemo<TreeNodeData[]>(
    () => [
      {
        id: 1,
        label: 'xxx科技有限公司',
        children: [
          {
            id: 2,
            label: '产品研发部',
            children: [
              { id: 3, label: '研发-前端' },
              { id: 4, label: '研发-后端' },
            ],
          },
          { id: 5, label: '销售部' },
          { id: 6, label: '财务部' },
        ],
      },
    ],
    []
  )

  return (
    <OkrTree
      data={data}
      nodeKey="id"
      direction="horizontal"
      showCollapsable
      defaultExpandAll
    />
  )
}

data 是数组,支持多个根节点。nodeKey 只在需要按 key 定位节点时才必须(受控展开、getNodegetCheckedKeys 等,见数据与类型)。

OKR 双向展开

'use client'

import { useMemo } from 'react'
import { OkrTree, type TreeNodeData } from 'react-okr-tree'
import 'react-okr-tree/style.css'

export default function App() {
  const data = useMemo<TreeNodeData[]>(
    () => [
      {
        id: 1,
        label: '2026 Q3 目标',
        children: [{ id: 11, label: 'KR1 完成核心链路重构' }],
      },
    ],
    []
  )
  const leftData = useMemo<TreeNodeData[]>(
    () => [
      {
        id: 2,
        label: '历史进展',
        children: [{ id: 21, label: 'Q2 KR3 已达成' }],
      },
    ],
    []
  )

  return (
    <OkrTree
      data={data}
      leftData={leftData}
      onlyBothTree
      direction="horizontal"
      nodeKey="id"
      showCollapsable
      defaultExpandAll
    />
  )
}

onlyBothTree 只在 direction="horizontal" 下有效,且必须给 leftData,否则抛 [Tree] leftData is required in onlyBothTreeleftData[0].children 挂到右树第一个根节点的左侧。

通过 ref 调方法

ref 拿到的是 OkrTreeHandle,28 个方法都在这里(完整表见 Methods):

'use client'

import { useMemo, useRef, useState } from 'react'
import { OkrTree, type OkrTreeHandle, type TreeNodeData } from 'react-okr-tree'
import 'react-okr-tree/style.css'

export default function App() {
  const tree = useRef<OkrTreeHandle>(null)
  const [keyword, setKeyword] = useState('')
  const data = useMemo<TreeNodeData[]>(() => [{ id: 1, label: '总部' }], [])

  return (
    <>
      <input
        value={keyword}
        onChange={e => {
          setKeyword(e.target.value)
          tree.current?.filter(e.target.value)
        }}
      />
      <button type="button" onClick={() => tree.current?.expandAll()}>
        全部展开
      </button>
      <OkrTree
        ref={tree}
        data={data}
        nodeKey="id"
        showCollapsable
        filterNodeMethod={(value, item) =>
          !value ? true : String(item.label).includes(String(value))
        }
      />
    </>
  )
}

filter 空值也要返回 true

filter('') 同样会对每个节点执行 filterNodeMethod,对空值返回 true 才能恢复全部显示。这是 element-ui 的语义,与源项目一致。

其他引入路径

路径产物写法
ESMdist/react-okr-tree.es.js + dist/index.d.tsimport { OkrTree } from 'react-okr-tree'
CJSdist/react-okr-tree.cjs + dist/index.d.ctsconst { OkrTree } = require('react-okr-tree')
UMDdist/react-okr-tree.umd.js<script> 直挂,全局变量 ReactOkrTree

包为 "type": "module",CJS 产物因此用 .cjs 扩展名,require()import 两条路都可用。 组件另有默认导出import OkrTree from 'react-okr-tree',与具名 OkrTree 是同一引用;UMD 下为 ReactOkrTree.default)——源项目的默认导出是 app.use() 用的插件对象,React 没有全局注册,就改成了组件本身。

CDN 用法(UMD 把 reactreact/jsx-runtime 外部化为全局 ReactReactJSXRuntime,React 官方 UMD 包不提供后者,需自行桥接):

<link rel="stylesheet" href="https://unpkg.com/react-okr-tree/dist/style.css" />
<script src="https://unpkg.com/react@18/umd/react.production.min.js"></script>
<script src="https://unpkg.com/react-dom@18/umd/react-dom.production.min.js"></script>
<script>
  window.ReactJSXRuntime = {
    Fragment: React.Fragment,
    jsx: (type, props, key) => React.createElement(type, key == null ? props : { ...props, key }),
    jsxs: (type, props, key) => React.createElement(type, key == null ? props : { ...props, key }),
  }
</script>
<script src="https://unpkg.com/react-okr-tree"></script>
<script>
  const { OkrTree } = window.ReactOkrTree
  ReactDOM.createRoot(document.getElementById('root')).render(
    React.createElement(OkrTree, { data: window.data, direction: 'horizontal', showCollapsable: true })
  )
</script>

CDN 下没有开发期警告

警告按 process.env.NODE_ENV !== 'production' 判定,浏览器直挂 UMD 时 process 不存在,因此视为生产环境、不输出任何开发期警告nodeKey 缺失、lazy 未配 loadtheme 名字拼错这类问题在 CDN 下没有提示,只有控制台外的静默行为;正式项目仍建议走打包器。

在 Next.js / SSR 中使用

组件在 renderToString 下可用:模块顶层不碰 window / document / matchMedia / ResizeObserver,与交互相关的 observer 和监听器只在挂载后注册,服务端快照走 useSyncExternalStoregetServerSnapshot

产物首行自带 'use client'

三种格式(es / cjs / umd)的产物首行都带 'use client' 指令(由 Rollup 的 output.banner 注入), 所以你可以在 Server Component 里直接 import 并渲染 OkrTree,不需要再包一层客户端组件。 但只要那个文件里传了回调onNodeClick / renderNode 等函数),它本身就必须是客户端组件—— 函数不能作为 props 跨过 RSC 边界,这条限制来自 React 而不是本包。CSS 同样在那一层 import。

exportImage()html-to-image 的动态导入、scrollToNode() 都是客户端专属能力,不要在服务端调用。

下一步

本页目录