npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

@tarsiidae/sport-socket

v1.0.46

Published

[![npm version](https://img.shields.io/npm/v/@tarsiidae/sport-socket.svg)](https://www.npmjs.com/package/@tarsiidae/sport-socket) [![license](https://img.shields.io/badge/license-MIT-green.svg)](LICENSE) [![TypeScript](https://img.shields.io/badge/TypeScr

Downloads

809

Readme

🏈 Sport Socket

npm version license TypeScript React lodash zustand axios ahooks vite-plugin-comlink

体育赛事组件和hooks,公共推送hooks

✨ 特性

  • 🚀 实时推送 - 基于WebSocket的实时数据推送
  • 🔐 自动认证 - 根据用户登录状态自动切换连接
  • 📱 React Hooks - 优雅的React Hooks API
  • 🤖 Web Worker - 使用Worker处理数据,不阻塞UI渲染
  • 🛒 智能购物车 - 自动管理投注项和串关逻辑
  • 📋 高阶组件 - 提供购物车、赛事列表、详情、单注、串关的高阶函数
  • 🔄 自动订阅 - 组件内部集成订阅和取消订阅逻辑,无需手动管理
  • 🎯 统一订阅 - 同一场赛事使用同一个赛事详情topic订阅(冠军除外)
  • 📦 完整类型 - 完整的TypeScript类型支持,覆盖所有hooks和事件推送

📦 安装

# 使用 yarn
yarn add @tarsiidae/sport-socket

# 使用 npm
npm install @tarsiidae/sport-socket

# 使用 pnpm
pnpm add @tarsiidae/sport-socket

🚀 快速开始

1. 初始化Provider

首先使用 PubProvider 包裹你的应用:

import { PubProvider } from '@tarsiidae/sport-socket';

function App() {
  return (
    <PubProvider>
      <YourApp />
    </PubProvider>
  );
}

2. 配置WebSocket连接

使用 useOkSocket Hook 配置连接:

import { useOkSocket } from '@tarsiidae/sport-socket';

function YourComponent() {
  useOkSocket({
    url: `${config?.wsHost}/api/ws/app/im`,
    token: g_token, // 用户token
    uid, // 用户id
    platformId: 1,
    lang: 'zh',
    oddsType: 'EU',
  });

  return <div>Your component content</div>;
}

💡 提示: 系统会根据用户是否登录自动切换socket连接状态

🛒 购物车功能

购物车高阶组件

import { withCart, useCart } from '@tarsiidae/sport-socket';

const Cart = withCart(() => {
  const { data, clearable, betType, toggleBetType, oddsType } = useCart();

  return (
    <div>
      <h2>购物车</h2>
      {clearable && <button onClick={clearCart}>清空购物车</button>}
      <button onClick={toggleBetType}>
        切换为{betType === 'parlay' ? '单关' : '串关'}
      </button>
      {data.map((item) => (
        <BetItem
          key={item.oid}
          mid={item.matchOdd?.matchInfo?.id ?? ''}
          oid={item.oid}
        />
      ))}
      {betType === 'parlay' ? <Parlays /> : <Single />}
    </div>
  );
});

投注项组件

import { withBetItem, useBetItem } from '@tarsiidae/sport-socket';

const BetItem = withBetItem(() => {
  const {
    odd,
    removeFromCart,
    cartItem,
    status,
    supportParlay,
    showSupportParlay,
  } = useBetItem();

  return (
    <div>
      <button onClick={removeFromCart}>移除</button>
      <div>{cartItem?.league?.name}</div>
      <div>
        {odd?.detailName}: {odd?.odds}
      </div>
      {showSupportParlay && (
        <span>{supportParlay ? '支持串关' : '不支持串关'}</span>
      )}
    </div>
  );
});

🏆 赛事功能

赛事列表组件

import { withMatch, useMatch } from '@tarsiidae/sport-socket';

const MatchItem = withMatch(() => {
  const {
    status,
    matchOdd,
    addCart,
    getIsSelected,
    countDownTime,
    handicapType,
  } = useMatch();

  return (
    <div>
      <div>
        {matchOdd?.matchInfo?.homeName} vs {matchOdd?.matchInfo?.awayName}
      </div>
      <div>状态: {status?.statusName}</div>
      <div>时间: {countDownTime}</div>
      {handicapType === 'europe' &&
        europePlays?.map((play) => (
          <EuroPlayItem key={play.playCateCode} {...play} />
        ))}
    </div>
  );
});

赛事详情组件

const MatchDetailItem = withMatch(() => {
  const { status, matchOdd, typeCodePlays, countDownTime } = useMatch();

  return (
    <div>
      <div>
        {matchOdd?.matchInfo?.homeName} vs {matchOdd?.matchInfo?.awayName}
      </div>
      <div>详情状态: {status?.statusName}</div>
      <div>时间: {countDownTime}</div>
      {typeCodePlays?.map((cate) => (
        <div key={cate.code}>
          <h3>{cate.name}</h3>
          {cate.plays?.map((play) => (
            <EuroPlayItem key={play.playCateCode} {...play} />
          ))}
        </div>
      ))}
    </div>
  );
});

🎯 单注和串关

单注组件

import { withSingle, useSingle } from '@tarsiidae/sport-socket';

const Single = withSingle(() => {
  const { singleBetAmount, betRange, winMoney, setSingleBetAmount } =
    useSingle();

  return (
    <div>
      <input
        type='text'
        value={singleBetAmount}
        onChange={(e) => setSingleBetAmount(e.target.value)}
        placeholder='请输入投注金额'
      />
      <div>
        投注范围: {betRange.min}-{betRange.max}
      </div>
      <div>可赢金额: {winMoney}</div>
    </div>
  );
});

串关组件

import { withParlays, useParlays } from '@tarsiidae/sport-socket';

const Parlays = withParlays(() => {
  const {
    totalBetCount,
    totalBetMoney,
    totalWinMoney,
    parlays,
    setParlayBetMoney,
  } = useParlays();
  const { data } = useCart();

  return (
    <div>
      {data.map((item) => (
        <BetItem
          key={item.oid}
          mid={item.matchOdd?.matchInfo?.id ?? ''}
          oid={item.oid}
        />
      ))}
      {parlays?.map((item) => (
        <ParlayItem key={item.combKey} {...item} />
      ))}
      <div>注数: {totalBetCount}</div>
      <div>投注: {totalBetMoney}</div>
      <div>可赢: {totalWinMoney}</div>
    </div>
  );
});

📡 事件订阅

公共推送和用户数据推送

import { usePub } from '@tarsiidae/sport-socket';

function YourComponent() {
  usePub((data, topic) => {
    if (data.eventType === 'USER_MONEY') {
      console.log('更新用户余额', data.money);
    }
    if (data.eventType === 'MATCH_STATUS_CHANGE') {
      console.log('赛事状态变化', data);
    }
  });

  return <div>Your component</div>;
}

🔧 高级功能

自动订阅管理

组件内部已经集成了完整的订阅和取消订阅逻辑:

  • 自动订阅: 当赛事进入视口时自动订阅
  • 自动取消: 当赛事离开视口且不在购物车中时自动取消订阅
  • 统一管理: 同一场赛事使用同一个赛事详情topic订阅(冠军除外)
  • 智能计数: 自动管理订阅计数,确保订阅状态正确

Web Worker 支持

所有数据处理都在 Web Worker 中进行,不会阻塞 UI 渲染

完整的 TypeScript 支持

所有 hooks 和组件都有完整的类型定义:

// 完整的类型支持
declare interface UseCartRes extends GetCombinationsRes {
  /** 购物车数据 */
  data: CartItemProps[];
  /** 购物车是否可清空 */
  clearable: boolean;
  /** 投注类型, 单关或者串关 */
  betType: BetType;
  /** 切换投注类型, 单关或者串关 */
  toggleBetType: () => void;
  /** 盘口 */
  oddsType: OddsType;
  /** 是否支持串关 */
  supportParlay: boolean;
  /** 串关投注列表 */
  betList: BetOddsListItem[];
  /** 串关投注金额列表 */
  stakeList: BetStakeList[];
  /** 单注可赢金额 */
  singleBetWinMoney: number;
  /** 单注预购赔率 */
  bookOdds: number | undefined;
  /** 单注预购投注信息 */
  bookOddsList: BetOddsListItem[];
  /** 单注预约投注可赢金额 */
  bookWinMoney: number;
}

📚 API 参考

Hooks

  • useOkSocket(config) - 配置 WebSocket 连接
  • usePub(callback) - 订阅公共推送事件
  • useMatch() - 获取赛事数据(在 withMatch 中使用)
  • useCart() - 获取购物车数据(在 withCart 中使用)
  • useBetItem() - 获取投注项数据(在 withBetItem 中使用)
  • useSingle() - 获取单注数据(在 withSingle 中使用)
  • useParlays() - 获取串关数据(在 withParlays 中使用)
  • useQuickBet() - 获取快速投注数据(在 withQuickBet 中使用)

高阶组件

  • withMatch(Component) - 赛事高阶组件
  • withCart(Component) - 购物车高阶组件
  • withBetItem(Component) - 投注项高阶组件
  • withSingle(Component) - 单注高阶组件
  • withParlays(Component) - 串关高阶组件
  • withQuickBet(Component) - 快捷投注高阶组件、、

工具函数

  • useSimpleSelectData(gameType) - 获取简单盘口选择数据
  • useHandicapType() - 获取盘口类型
  • useClearCart() - 清空购物车
  • useCartLen() - 获取购物车长度
  • useGetQuickBetInfo() - 获取快速投注信息
  • useEnableQuickBet() - 获取和设置是否启用快速投注
  • useQuickBetAmount() - 获取和设置快速投注金额
  • useParlayOrderInfo() - 获取串关投注成功后,订单信息; 在串关投注成功后,保存数据
  • useBookOddsLimit() - 获取单注预购赔率限制信息
    • isBookOddsLowerThanCurrentOdds - 单注预购赔率是否低于当前赔率
    • isBookOddsHigherThanMax - 单注预购赔率是否高于最大值(欧洲盘255,香港盘254)
  • useIsSingleEndLen() - 获取单注的末位比分长度
  • useEnableBooking() - 获取和设置是否开启预约投注
    • enableBooking - 是否开启预约投注
    • setEnableBooking - 设置是否开启预约投注
  • useSetEndOddBetFailed() - 设置末位比分投注失败

📄 许可证

MIT License - 详见 LICENSE 文件