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

trrn-h

v0.3.0

Published

Closure-based React component wrapper. Closures instead of useState, update() instead of setState.

Downloads

228

Readme

trrn-h

闭包变量 替代 useState,用 update() 替代 setState。render 函数中仍可自由使用 React hooks。实验性项目。

import { defineComponent } from "trrn-h";

const Counter = defineComponent(({ initial = 0 }, { update }) => {
  let count = initial; // 闭包 = 状态(只初始化一次)
  return () => (
    // render 函数 = 每次渲染执行
    <button
      onClick={() => {
        count++;
        update();
      }}
    >
      {count}
    </button>
  );
});

核心概念

defineComponent((props, ctx) => {
  // ┌─ 工厂函数 ─────────────────────────┐
  // │ 只执行一次,闭包变量 = 组件状态     │
  // │ ctx.onMount / ctx.onUnmount 注册    │
  // └─────────────────────────────────────┘
  return (props) => {
    // ┌─ render 函数 ────────────────────┐
    // │ 每次渲染执行,可使用 React hooks │
    // │ props 始终是最新值               │
    // └──────────────────────────────────┘
  };
});

| API | 作用 | | ------------------- | ---------------------------------------------- | | ctx.update() | 触发重渲染(修改闭包后调用) | | ctx.onMount(fn) | DOM 挂载后回调(基于 useEffect,SSR 不执行) | | ctx.onUnmount(fn) | 卸载时清理 | | render 中的 hooks | useContextuseMemouse() 等全部可用 |

工厂函数不能用 hooks(不在组件顶层),render 函数全都能用——闭包和 hooks 按需选用。

render 函数的 props 始终是最新值

工厂函数的 props 参数只初始化一次(闭包捕获),而 render 函数的 props 参数每次渲染都是最新的

const Comp = defineComponent(({ label }: { label: string }) => {
  // 外层 label — 仅初始值(闭包捕获后不再更新)
  return ({ label }) => <div>{label}</div>;
  //       ^^^^^^^^  同名解构参数遮蔽外层变量,每次渲染都是最新值
});

利用 JavaScript 的变量遮蔽(shadowing),render 函数参数解构与工厂函数解构参数同名时,render 函数内部引用的是参数(最新值),而非外层闭包变量(初始值)。

父组件更新 props 时,React 重新调用组件函数,render 函数收到最新的 props。任何依赖 props 的渲染逻辑都要用 render 函数的参数,不要用外层工厂函数的参数。


安装

npm install trrn-h
# 或
pnpm add trrn-h
// tsconfig.json
{ "compilerOptions": { "jsx": "react-jsx" } }
// 入口
import { createRoot } from "react-dom/client";
createRoot(document.getElementById("app")!).render(<App />);

Preact 项目配置 @preact/preset-vitereact 自动映射到 preact/compat 即可使用。


完整示例

const Timer = defineComponent((_, { onMount, onUnmount, update }) => {
  let seconds = 0;
  let timerId: ReturnType<typeof setInterval>;

  onMount(() => {
    timerId = setInterval(() => {
      seconds++;
      update();
    }, 1000);
  });
  onUnmount(() => clearInterval(timerId));

  return () => <div>{seconds}s</div>;
});
// hooks + 闭包联用
const DataList = defineComponent((_, { update, onMount }) => {
  let items = [];

  onMount(async () => {
    items = await fetch("/api/data").then((r) => r.json());
    update();
  });

  return () => {
    const theme = useContext(ThemeCtx);       // hook
    const filtered = useMemo(() => items.filter(...), [items]);
    return <div className={theme}>{filtered}</div>;
  };
});

SSR

天然兼容。onMount 在 SSR 期间不执行(基于 useEffect),hydrate 后自动触发。update() 在服务端安全调用但不产生效果。


常见陷阱

| # | 陷阱 | 正确 | | --- | --------------------------------------- | ---------------------------------------------- | | 1 | 修改闭包后忘了 update() | count++; update(); | | 2 | render 函数中调 update()(无限循环) | 只在事件/异步/生命周期中调用 | | 3 | render 函数用了外层 props(永远初始值) | 用 ({ label }) => label 同名解构遮蔽外层变量 |


License

MIT