Skip to content

Instantly share code, notes, and snippets.

Embed
What would you like to do?

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' }
]
}
@lukeed
Copy link

lukeed commented Mar 30, 2017

🤔 Why is this a gist & not a repo?

@jquense
Copy link

jquense commented Mar 30, 2017

gists are repos ;)

@lukeed
Copy link

lukeed commented Mar 30, 2017

But I have to search for them separately 😉

@developit
Copy link
Author

developit commented Mar 30, 2017

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

developit commented Mar 30, 2017

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

@mehmetkose
Copy link

mehmetkose commented Mar 31, 2017

LOL

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