
Security News
Another Round of TEA Protocol Spam Floods npm, But It’s Not a Worm
Recent coverage mislabels the latest TEA protocol spam as a worm. Here’s what’s actually happening.
Type-safe React application state library with zero setup. Powered by
Object.defineProperty.
npm i act0
# yarn add act0
import Act0 from 'act0';
// 1. Define your root store
// Act0 adds "use" method to the RootStore instance, that's all what it does
class RootStore extends Act0 {
count: 1,
}
const store = new RootStore();
// 2. Use
export default () => {
const count = store.use('count'); // same as store['count'] but reactive
return (
<div onClick={() => store.count++}>Clicks: {count}</div>
);
}
Create your store with ES6 classes extended by Act0. It's recommended to split it into multiple objects that I call "sub-stores". In the example below Users and Companies are sub-stores. Level of nesting is unlimited as for any other JavaScript object.
// store.ts
import Act0 from 'act0';
class Users extends Act0 {
ids = [1, 2, 3];
readonly loadUsers = () => fetch('/users')
}
class Companies extends Act0 {
name = 'My Company';
}
class RootStore extends Act0 {
readonly users = new Users();
readonly companies = new Companies();
readonly increment = () => this.count++;
readonly decrement = () => this.count--;
count = 0;
}
const store = new RootStore();
export default store;
Use readonly prefix to protect class members to be reassigned.
Use use method to access store object properties in your component.
const MyComponent = () => {
const count = store.use('count');
const ids = store.users.use('ids');
const name = store.companies.use('ids');
// ...
To change value, assign a new value.
store.count++;
store.users.ids = [...store.users.ids, 4]
Call methods for actions.
useEffect(() => {
store.users.loadUsers().then(() => {
store.decrement();
// ...
});
}, []); // no dependencies for methods
Pass values returned from use as dependencies for hooks.
const count = store.use('count');
const callback = useCallback(() => { console.log(count); }, [count])
You can split sub-stores into multiple files and access root store using first argument.
// ./store/index.ts
import Users from './Users';
import Companies from './Companies';
export class RootStore {
readonly users: Users;
readonly companies: Companies;
constructor() {
this.users = new Users(this);
this.companies = new Companies(this);
}
}
// ./store/Users.ts (Companies.ts is similar)
import type { RootStore } from '.'; // "import type" avoids circular errors with ESLint
export default class Users {
#store: RootStore;
constructor(store: RootStore) {
this.#store = store;
}
readonly loadUsers() {
// you have access to any part of the store
const something = this.#store.companies.doSomething();
// ...
}
}
I recommend to destructure all methods that are going to be called to make it obvious and to write less code at hooks and components.
const MyComponent = ({ id }) => {
const { increment, decrement, users: { loadUsers } } = store;
// ...
}
or better
const { increment, decrement, users: { loadUsers } } = store;
const MyComponent = ({ id }) => {
// ...
}
If you don't want to define class you can use this static method. Act0.of<T>(data?: T): Act0 & T returns Act0 instance with use method and uses firtst argument as initial values.
class RootStore extends Act0 {
readonly coordinates = Act0.of({ x: 0, y: 100 });
// ...
const MyComponent = () => {
const x = store.coordinates.use('x');
const y = store.coordinates.use('y');
// ..
// store.coordinates.x = 100;
You can also define custom record:
class RootStore extends Act0 {
data: Act0.of<Record<string, Item>>();
// ...
}
// ...
And acces values as usual:
const MyComponent = ({ id }) => {
const item = store.data.use(id); // same as store.data[id] but reactive
// ...
// store.data[id] = someValue; // triggers the component to re-render
For a very small app you can define your entire application state using Act0.of method (also exported as a constant).
// store.ts
import { of as act } from 'act0';
const store = act({
count: 1,
companies: act({
name: 'My company',
someMethod() { /* ... */ }
}),
});
export default store;
import store from './store';
const MyComponent = () => {
const count = store.use('count'); // same as store['count'] but reactive
const name = store.companies.use('name'); // same as store.companies['name'] but reactive
// store.companies.someMethod();
FAQs
Application state
We found that ai0 demonstrated a not healthy version release cadence and project activity because the last version was released a year ago. It has 1 open source maintainer collaborating on the project.
Did you know?

Socket for GitHub automatically highlights issues in each pull request and monitors the health of all your open source dependencies. Discover the contents of your packages and block harmful activity before you install or update your dependencies.

Security News
Recent coverage mislabels the latest TEA protocol spam as a worm. Here’s what’s actually happening.

Security News
PyPI adds Trusted Publishing support for GitLab Self-Managed as adoption reaches 25% of uploads

Research
/Security News
A malicious Chrome extension posing as an Ethereum wallet steals seed phrases by encoding them into Sui transactions, enabling full wallet takeover.