{"id":20022237,"url":"https://github.com/lucifier129/costate","last_synced_at":"2025-08-02T09:36:16.617Z","repository":{"id":57209678,"uuid":"198799212","full_name":"Lucifier129/costate","owner":"Lucifier129","description":"A state management library for react inspired by vue 3.0 reactivity api and immer","archived":false,"fork":false,"pushed_at":"2020-05-18T09:11:48.000Z","size":673,"stargazers_count":7,"open_issues_count":0,"forks_count":0,"subscribers_count":3,"default_branch":"master","last_synced_at":"2025-05-05T01:36:47.593Z","etag":null,"topics":["react","react-hooks","reactivity","state-management"],"latest_commit_sha":null,"homepage":"","language":"TypeScript","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/Lucifier129.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null}},"created_at":"2019-07-25T09:20:35.000Z","updated_at":"2023-07-27T12:05:41.000Z","dependencies_parsed_at":"2022-09-01T08:51:00.976Z","dependency_job_id":null,"html_url":"https://github.com/Lucifier129/costate","commit_stats":null,"previous_names":[],"tags_count":0,"template":false,"template_full_name":null,"purl":"pkg:github/Lucifier129/costate","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/Lucifier129%2Fcostate","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/Lucifier129%2Fcostate/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/Lucifier129%2Fcostate/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/Lucifier129%2Fcostate/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/Lucifier129","download_url":"https://codeload.github.com/Lucifier129/costate/tar.gz/refs/heads/master","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/Lucifier129%2Fcostate/sbom","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":268363005,"owners_count":24238546,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","status":"online","status_checked_at":"2025-08-02T02:00:12.353Z","response_time":74,"last_error":null,"robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":true,"can_crawl_api":true,"host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["react","react-hooks","reactivity","state-management"],"created_at":"2024-11-13T08:39:38.958Z","updated_at":"2025-08-02T09:36:16.575Z","avatar_url":"https://github.com/Lucifier129.png","language":"TypeScript","funding_links":[],"categories":[],"sub_categories":[],"readme":"# Welcome to costate 👋\n\n[![npm version](https://img.shields.io/npm/v/costate.svg?style=flat)](https://www.npmjs.com/package/costate)\n[![Build Status](https://travis-ci.org/Lucifier129/costate.svg?branch=master)](https://travis-ci.org/Lucifier129/costate)\n[![Documentation](https://img.shields.io/badge/documentation-yes-brightgreen.svg)](https://github.com/Lucifier129/costate#readme)\n[![Maintenance](https://img.shields.io/badge/Maintained%3F-yes-green.svg)](https://github.com/Lucifier129/costate/graphs/commit-activity)\n[![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](https://github.com/Lucifier129/costate/blob/master/LICENSE)\n[![Twitter: guyingjie129](https://img.shields.io/twitter/follow/guyingjie129.svg?style=social)](https://twitter.com/guyingjie129)\n\n\u003e A state management library for react inspired by vue 3.0 reactivity api and immer\n\n**costate** is a tiny package that allows you to work with immutable state in a more reactive way.\n\n### 🏠 [Homepage](https://github.com/Lucifier129/costate#readme)\n\n## Features\n\n- mutate costate to derive the next immutable state reactively\n- write code in idiomatic javascript style\n- no need to centralize all of update-state/reducer function in React Component\n\n## Environment Requirement\n\n- ES2015 Proxy\n- ES0215 Map\n- ES2015 Symbol\n\n[Can I Use Proxy?](https://caniuse.com/#search=Proxy)\n\n## Install\n\n```sh\nnpm install --save costate\n```\n\n```sh\nyarn add costate\n```\n\n## [API DOCS](/docs/API.md)\n\n## Usage\n\n```javascript\nimport { createCostate, watch } from 'costate'\n\n// costate is reactive\nconst costate = createCostate({ a: 1 })\n\n// state is immutable\nwatch(costate, state =\u003e {\n  console.log(`state.a is: ${state.a}`)\n})\n\n// mutate costate will emit the next immutable state to watcher\ncostate.a += 1\n```\n\n## Why costate is useful?\n\nThink about **costate** + **react-hooks**!\n\n### Counter\n\n- [demo](https://lucifier129.github.io/costate-examples/build/#Counter)\n- [source-code](https://github.com/Lucifier129/costate-examples/blob/master/src/demos/Counter.js)\n\n```javascript\nimport * as React from 'react'\nimport { co } from 'costate'\nimport { useCostate } from 'costate/react'\n\nfunction Counter() {\n  // useCostate instead of React.useState\n  // state is always immutable\n  let state = useCostate({ count: 0 })\n\n  let handleIncre = () =\u003e {\n    // pass state to co, then got the costate which is reactive\n    // mutate costate will cause re-render and receive the next state\n    co(state).count += 1\n  }\n\n  let handleDecre = () =\u003e {\n    co(state).count -= 1\n  }\n\n  return (\n    \u003c\u003e\n      \u003cbutton onClick={handleIncre}\u003e+1\u003c/button\u003e\n      {state.count}\n      \u003cbutton onClick={handleDecre}\u003e-1\u003c/button\u003e\n    \u003c/\u003e\n  )\n}\n```\n\n### TodoApp\n\n- [demo](https://lucifier129.github.io/costate-examples/build/#TodoApp)\n- [source-code](https://github.com/Lucifier129/costate-examples/blob/master/src/demos/TodoApp.js)\n\n```javascript\nexport default function App() {\n  // initialize todo-app state\n  let state = useCostate({\n    todos: [],\n    text: {\n      value: ''\n    }\n  })\n\n  useSessionStorage({\n    key: 'todos-json',\n    getter: () =\u003e state,\n    setter: source =\u003e Object.assign(co(state), source)\n  })\n\n  let handleAddTodo = () =\u003e {\n    if (!state.text.value) {\n      return alert('empty content')\n    }\n\n    // wrap by co before mutating\n    co(state).todos.push({\n      id: Date.now(),\n      content: state.text.value,\n      completed: false\n    })\n    co(state).text.value = ''\n  }\n\n  let handleKeyUp = event =\u003e {\n    if (event.key === 'Enter') {\n      handleAddTodo()\n    }\n  }\n\n  let handleToggleAll = () =\u003e {\n    let hasActiveItem = state.todos.some(todo =\u003e !todo.completed)\n    // wrap by co before mutating\n    co(state).todos.forEach(todo =\u003e {\n      todo.completed = hasActiveItem\n    })\n  }\n\n  return (\n    \u003c\u003e\n      \u003cdiv\u003e\n        \u003cTodoInput text={state.text} onKeyUp={handleKeyUp} /\u003e\n        \u003cbutton onClick={handleAddTodo}\u003eadd\u003c/button\u003e\n        \u003cbutton onClick={handleToggleAll}\u003etoggle-all\u003c/button\u003e\n      \u003c/div\u003e\n      \u003cTodos todos={state.todos} /\u003e\n      \u003cFooter todos={state.todos} /\u003e\n    \u003c/\u003e\n  )\n}\n\nfunction Todos({ todos }) {\n  return (\n    \u003cul\u003e\n      {todos.map(todo =\u003e {\n        return \u003cTodo key={todo.id} todo={todo} /\u003e\n      })}\n    \u003c/ul\u003e\n  )\n}\n\nfunction Todo({ todo }) {\n  // you can create any costate you want\n  // be careful, costate must be object or array\n  let edit = useCostate({ value: false })\n  let text = useCostate({ value: '' })\n\n  let handleEdit = () =\u003e {\n    // wrap by co before mutating\n    co(edit).value = !edit.value\n    co(text).value = todo.content\n  }\n\n  let handleEdited = () =\u003e {\n    co(edit).value = false\n    // magic happen!!\n    // we don't need TodoApp to pass updateTodo function down to Todo\n    // we just like todo is local state, wrap by co before mutating it\n    // then it will cause TodoApp drived new state and re-render\n    co(todo).content = text.value\n  }\n\n  let handleKeyUp = event =\u003e {\n    if (event.key === 'Enter') {\n      handleEdited()\n    }\n  }\n\n  let handleRemove = () =\u003e {\n    // we don't need TodoApp to pass removeTodo function down to Todo\n    // cotodo can be delete by remove function\n    remove(co(todo))\n  }\n\n  let handleToggle = () =\u003e {\n    co(todo).completed = !todo.completed\n  }\n\n  return (\n    \u003cli\u003e\n      \u003cbutton onClick={handleRemove}\u003eremove\u003c/button\u003e\n      \u003cbutton onClick={handleToggle}\u003e{todo.completed ? 'completed' : 'active'}\u003c/button\u003e\n      {edit.value \u0026\u0026 \u003cTodoInput text={text} onBlur={handleEdited} onKeyUp={handleKeyUp} /\u003e}\n      {!edit.value \u0026\u0026 \u003cspan onClick={handleEdit}\u003e{todo.content}\u003c/span\u003e}\n    \u003c/li\u003e\n  )\n}\n\nfunction TodoInput({ text, ...props }) {\n  let handleChange = event =\u003e {\n    co(text).value = event.target.value\n  }\n  return \u003cinput type=\"text\" {...props} onChange={handleChange} value={text.value} /\u003e\n}\n\nfunction Footer({ todos }) {\n  let activeItems = todos.filter(todo =\u003e !todo.completed)\n  let completedItems = todos.filter(todo =\u003e todo.completed)\n\n  let handleClearCompleted = () =\u003e {\n    ;[...completedItems].reverse().forEach(item =\u003e remove(co(item)))\n  }\n\n  return (\n    \u003cdiv\u003e\n      {activeItems.length} item{activeItems.length \u003e 1 \u0026\u0026 's'} left |{' '}\n      {completedItems.length \u003e 0 \u0026\u0026 \u003cbutton onClick={handleClearCompleted}\u003eClear completed\u003c/button\u003e}\n    \u003c/div\u003e\n  )\n}\n```\n\n## Caveat\n\n- `createCostate(state)` only accept object or array as arguemnt\n\n## Author\n\n👤 **Jade Gu**\n\n- Twitter: [@guyingjie129](https://twitter.com/guyingjie129)\n- Github: [@Lucifier129](https://github.com/Lucifier129)\n\n## 🤝 Contributing\n\nContributions, issues and feature requests are welcome!\n\nFeel free to check [issues page](https://github.com/Lucifier129/costate/issues).\n\n## Show your support\n\nGive a ⭐️ if this project helped you!\n\n## 📝 License\n\nCopyright © 2019 [Jade Gu](https://github.com/Lucifier129).\n\nThis project is [MIT](https://github.com/Lucifier129/costate/blob/master/LICENSE) licensed.\n\n---\n\n_This README was generated with ❤️ by [readme-md-generator](https://github.com/kefranabg/readme-md-generator)_\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Flucifier129%2Fcostate","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Flucifier129%2Fcostate","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Flucifier129%2Fcostate/lists"}