babel-plugin-react-label-sugar

A simple React Label Sugar just for fun :) 😄

online babel playground

stackblitz example

Quick Start

# install
npm install --save-dev babel-plugin-react-label-sugar

Add plugin in .babelrc

{
  "plugins": ["babel-plugin-react-label-sugar"]
}

If you use vite, add this config to vite react plugin (swc is not supported)

{
    plugins: [react({ babel: { babelrc: true } })],
}

(optional) custom options

{ 
  "refLabel": "$",              // default is "ref"
  "watchLabel": "on",           // default is "watch"
  "stateFactory": "useState",   // default is "React.useState"
  "memoFactory": "useMemo",     // default is "React.useMemo"
  "effectFactory": "useEffect", // default is "React.useEffect"
  "ignoreMemberExpr": false,    // default is true, will skip object value mutation
}

Examples

// before
ref: count = 0;
watch: doubled = count => count * 2
watch: count => console.log(count)

count++;
count = 10;
// after
const [count, setCount] = useState(0);
const doubled = useMemo(() => count * 2, [count]);
useEffect(() => {
  console.log(count);
}, [count])

setCount(count => count + 1);
setCount(count => 10);

Q&A

Q: Can I use it to declare objects like Vue ref?

A: No, this plugin doesn't do anything on your code, but transpile the label expression to useState function call.

// so you must use it like this
$: student = { name: "xyy" };

<input onChange={e => student = { ...student, name: e.target.value }}></input>

But you can use immer to achieve this

Set .babelrc { "ignoreMemberExpr": false, "stateFactory": "useImmer" }

// before
ref: obj = { count: 0 }
obj.count++;

// after
const [obj, setObj] = useImmer({ count: 0 });
setObj((obj) => { obj.count++ });

Tips

If your program gets stuck while running the following command value = e.target.value. This is probably because you are using React 16. This plugin uses function arguments (eg. setState(() => 1)) by default when handling data changes, and React 16 loses event references due to its event pool.

React 17, on the other hand, behaves just fine.

Todo List

  • support useImmer, transpile obj.value = 1 to setObject(obj => obj.value = 1)
  • more labels, auto generate dependency list.
  • custom labels