Skip to content

Instantly share code, notes, and snippets.

@developit
Last active December 10, 2020 18:48
Show Gist options
  • Star 49 You must be signed in to star a gist
  • Fork 3 You must be signed in to fork a gist
  • Save developit/006c58bba02be99e9a2ea1aca536dd33 to your computer and use it in GitHub Desktop.
Save developit/006c58bba02be99e9a2ea1aca536dd33 to your computer and use it in GitHub Desktop.

tracked npm

@tracked is a decorator for Preact that makes working with state values no different than properties on your component instance.

It's one 300 byte function that creates a getter/setter alias into state/setState() for a given key, with an optional initial value. The "magic" here is simply that it works as a property decorator rather than a function, so it appears to integrate directly into the language.

tracked has no dependencies and works with any component implementation that uses this.state and this.setState().

Installation

It's available on npm as tracked (can you believe it?):

npm install --save tracked

Example

Bask in the glory:

class Example extends Component {
  // initialize state.count with a value:
  @tracked count = 1000;
  
  updateCount = e => {
    // updates state.count via setState()
    this.count = e.target.value;
  };
  
  render() {
    return (
      <input
        value={this.count}  // it's just a value!
        onChange={this.updateCount}
      />
    );
  }
}

Ok but seriously, how does it work?

It's a decorator, but you really don't need to care about that to see how things are working. Here's what's going on:

// this terse syntax:
@tracked a = 1;

// ... produces this:
Object.defineProperty(this, 'a', {
    get: () => this.state.a,
    set: v => this.setState({ a: v })
});

License

MIT probably

dist
node_modules
.DS_Store
export default function tracked(obj, key, desc) {
let setter = {};
function initialize() {
Object.defineProperty(this, key, {
configurable: true,
get: () => this.state[key],
set: v => { setter[key] = v; this.setState(setter); }
});
return this.state[key] = desc.initializer();
}
return {
configurable: true,
set(v) {
initialize.call(this);
this[key] = v;
},
get: initialize
};
}
{
"name": "tracked",
"version": "1.1.1",
"description": "A 300 byte @tracked property decorator for Preact.",
"main": "dist/tracked.umd.js",
"modules": "index.js",
"scripts": {
"build": "rollup -c --environment FORMAT:es && rollup -c"
},
"keywords": [
"preact",
"glimmer",
"tracked",
"state"
],
"files": [
"dist",
"index.js"
],
"author": "Jason Miller <jason@developit.ca> (http://jasonformat.com)",
"license": "ISC",
"devDependencies": {
"rollup": "^0.41.6",
"rollup-plugin-buble": "^0.15.0",
"rollup-plugin-uglify": "^1.0.1"
}
}
import uglify from 'rollup-plugin-uglify';
import buble from 'rollup-plugin-buble';
export default {
exports: 'default',
useStrict: false,
entry: 'index.js',
moduleName: 'tracked',
plugins: [
buble(),
process.env.FORMAT!='es' && uglify()
].filter(Boolean),
targets: process.env.FORMAT=='es' ? [
{ format:'es', dest:'dist/tracked.es.js' }
] : [
{ format:'cjs', dest:'dist/tracked.js' },
{ format:'umd', dest:'dist/tracked.umd.js' }
]
}
@developit
Copy link
Author

It's a long story.

@thysultan
Copy link

thysultan commented Mar 30, 2017

I wonder if you can go further and make this.state itself a getter/setter that returns an object that will be passed to setState.

this._state = {};
Object.defineProperty(this, 'state', {
    get: () => setTimeout(() => this.setState(this._state)), this._state,
    set: v => setTimeout(() => this.setState(this._state)), !v ? this._state : Object.assign(this._state, v)
});

@developit
Copy link
Author

@thysultan people might die from the lack of get/set parity there haha

@mehmetkose
Copy link

LOL

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment