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

@tangmu1121/r-model

v0.1.0

Published

Vue-style v-model experience for React via compile-time transform + runtime hooks.

Readme

r-model

一个让 React 拥有几乎和 Vue3 一样的双向绑定书写体验的小库,但属性名改为 r-model,和包名保持统一:

  • 运行时:提供 useVModel / useModel 等 Hook
  • 编译期:提供 Babel 插件,把 <Comp v-model={foo} /> 自动改写成对应的 props + setter

目前主要支持在「自定义组件」上使用 r-model(例如 <MyInput r-model={value} />)。

安装

npm install r-model
# 或
yarn add r-model

注意:本库假设你的 React 版本 >=16.8(支持 Hooks)。

API 概览

  • 编译期:Babel 插件
    • <Comp r-model={foo} /><Comp modelValue={foo} onUpdateModelValue={val => setFoo(val)} />
    • 要求:在同一组件函数里用 useState 定义 const [foo, setFoo] = useState(...)(或者至少有一个 setFoo 函数)。
  • 运行时:Hooks
    • useVModel(props, propName)
      • 用在 子组件内部,把 props.modelValue / props.xxx 和对应的 onUpdateModelValue / onUpdateXxx 封成 [value, setValue]
    • useModel({ value?, defaultValue?, onChange? })
      • 用在 你自己写的受控/非受控组件内部,统一处理 value / defaultValue / onChange 逻辑。

配置 Babel 插件(r-model 语法)

假设你使用 Babel(或 Vite/Next 等内置 Babel 流程),在 Babel 配置中加入:

{
  "plugins": ["r-model"]
}

或明确指定路径:

{
  "plugins": ["r-model/dist/babel.cjs"]
}

然后在 React 组件里就可以直接写:

import React, { useState } from "react";
import { MyInput } from "./MyInput";

export function Demo() {
  const [name, setName] = useState("");

  return <MyInput r-model={name} />;
}

插件会在编译期把上面的代码变成:

<MyInput
  modelValue={name}
  onUpdateModelValue={(val) => setName(val)}
/>

约定:setter 按变量名推断,例如 foosetFoouserNamesetUserName。你需要确保组件里真的有对应的 setXxx 存在,一般通过 const [foo, setFoo] = useState(...)


useVModel:在子组件中消费 r-model

父组件用法

import React from "react";
import { useVModel } from "r-model";

interface TextInputProps {
  // 由编译器自动生成:
  // <TextInput r-model={name} />
  // → modelValue={name} onUpdateModelValue={val => setName(val)}
  modelValue: string;
  onUpdateModelValue?: (v: string) => void;
}

export function TextInput(props: TextInputProps) {
  const [value, setValue] = useVModel(props, "modelValue");

  return (
    <input
      value={value}
      onChange={(e) => setValue(e.target.value)}
      placeholder="请输入名字"
    />
  );
}

将来可扩展为 r-model:namename + onUpdateName 等多模型场景。


useModel:统一 value / defaultValue / onChange

这个 Hook 主要给「组件内部」使用,帮助你轻松支持:

  • 受控模式:由外部传入 value + onChange
  • 非受控模式:由外部传入 defaultValue,内部自己维护 state

示例:可受控的 Counter

import React from "react";
import { useModel, ModelProps } from "r-model";

interface CounterProps extends ModelProps<number> {
  step?: number;
}

export function Counter(props: CounterProps) {
  const [count, setCount] = useModel<number>(props);
  const step = props.step ?? 1;

  return (
    <div>
      <button onClick={() => setCount((count ?? 0) - step)}>-</button>
      <span style={{ margin: "0 8px" }}>{count ?? 0}</span>
      <button onClick={() => setCount((count ?? 0) + step)}>+</button>
    </div>
  );
}

使用:非受控

// 内部自己管 state
<Counter defaultValue={10} />

使用:受控

const [count, setCount] = useState(0);

<Counter value={count} onChange={setCount} />;

在你自己的项目中使用

  1. 在你的库或项目中引入:

    import { useVModel, useModel } from "r-model";
  2. 对于「可复用输入组件」,推荐:

    • 父组件:直接写 <MyInput r-model={value} />
    • 编译器:自动生成 modelValue / onUpdateModelValue
    • 子组件:通过 useVModel 把它们封装成 [value, setValue]
  3. 对于「更复杂的组件(例如带默认值、可受控/非受控切换)」:

    • 在组件内部使用 useModel,暴露出 value / defaultValue / onChange 这套标准接口。

未来可以扩展的方向

  • 支持类似 v-model:xxx 的多字段绑定(如 model:checkedmodel:value
  • 支持对嵌套对象路径(如 "user.name")做不可变更新
  • 集成常见 UI 库(antd, MUI 等)的最佳实践示例

如果你需要这些功能,可以在你的工程里基于当前实现继续扩展,或者发布到 npm 作为你自己的私有/开源包。package.jsontsconfig.json 和基础源码已经在本仓库中为你准备好了,直接执行:

npm install
npm run build

即可打包生成 dist,然后用 npm publish 发布到 npm。