diff --git a/CHANGELOG.md b/CHANGELOG.md
index a27facc..70fd8d9 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -12,6 +12,8 @@ All notable changes to `hy-event-store` are documented in this file.
the original callback reference.
- Document the supported package-name import path for WeChat Mini Programs and
add a package-root entry-point regression test.
+- Add `getState(key)` for reading a declared state value without subscribing to
+ updates.
## 1.4.0 - 2026-08-19
diff --git a/README.md b/README.md
index c2e8fe7..43fc5fd 100644
--- a/README.md
+++ b/README.md
@@ -147,6 +147,16 @@ async function loadData() {
onState invokes its callback immediately with the current value,
then again when that key changes.
+Read one declared value without subscribing to updates:
+
+~~~js
+const status = store.getState("status")
+~~~
+
+getState does not notify listeners and throws for an undeclared key.
+It returns the current value as-is; it is not a deep clone or deep read-only
+snapshot.
+
Subscribe to several keys with onStates:
~~~js
@@ -190,6 +200,7 @@ Create a store with a required state object and an optional
| onStates(keys, callback) | Subscribe to several declared keys and immediately receive their initial snapshot. Later callbacks contain changed keys only. |
| offStates(keys, callback) | Remove a multi-key subscription. |
| setState(key, value) | Update a declared state key. |
+| getState(key) | Read one declared state value without subscribing. |
| dispatch(actionName, ...args) | Run an action and return its value unchanged, including a Promise. |
## Usage notes
diff --git a/README.zh-CN.md b/README.zh-CN.md
index af4965c..e72b144 100644
--- a/README.zh-CN.md
+++ b/README.zh-CN.md
@@ -140,6 +140,15 @@ async function loadData() {
onState 会先立即以当前值调用一次回调,之后在对应状态键变更时再次调用。
+如需读取一个已声明的状态值,但不订阅更新,可以使用:
+
+~~~js
+const status = store.getState("status")
+~~~
+
+getState 不会通知监听器,读取未声明的键时会抛出错误。它会原样
+返回当前值,不会创建深拷贝,也不是深层只读快照。
+
使用 onStates 订阅多个状态键:
~~~js
@@ -180,6 +189,7 @@ store.offStates(["count", "status"], renderProfile)
| onStates(keys, callback) | 订阅多个已声明状态键,并立即收到初始快照;后续回调只包含变化的键。 |
| offStates(keys, callback) | 取消多状态订阅。 |
| setState(key, value) | 更新已声明的状态键。 |
+| getState(key) | 读取一个已声明的状态值,不建立订阅。 |
| dispatch(actionName, ...args) | 运行 action,并原样返回它的结果,包括 Promise。 |
## 使用注意事项
diff --git a/ROADMAP.md b/ROADMAP.md
index 5ad183c..3d3cea6 100644
--- a/ROADMAP.md
+++ b/ROADMAP.md
@@ -26,7 +26,7 @@ These items need API design or compatibility research before implementation:
- Document and test CommonJS and Mini Program import paths; native ESM remains
a separate compatibility item
([#27](https://github.com/coderwhy/hy-event-store/issues/27)).
-- Evaluate a read-only state getter
+- Add a read-only state getter
([#26](https://github.com/coderwhy/hy-event-store/issues/26)).
- Explore an opt-in full snapshot for `onStates`
([#24](https://github.com/coderwhy/hy-event-store/issues/24)).
diff --git a/Test/event-store.test.js b/Test/event-store.test.js
index 9ab3c8e..83e2e1a 100644
--- a/Test/event-store.test.js
+++ b/Test/event-store.test.js
@@ -53,6 +53,21 @@ test("onStates reports the initial snapshot and changed key until removed", () =
])
})
+test("getState reads a declared value without notifying listeners", () => {
+ const store = new HYEventStore({ state: { count: 0 } })
+ let notifications = 0
+ store.onState("count", () => {
+ notifications += 1
+ })
+
+ assert.equal(store.getState("count"), 0)
+ assert.equal(notifications, 1)
+ store.setState("count", 1)
+ assert.equal(store.getState("count"), 1)
+ assert.equal(notifications, 2)
+ assert.throws(() => store.getState("missing"), /does not contain/)
+})
+
test("dispatch returns synchronous action results", () => {
const store = new HYEventStore({
state: { count: 1 },
diff --git a/Test/types.test.ts b/Test/types.test.ts
index 3dba997..0217cce 100644
--- a/Test/types.test.ts
+++ b/Test/types.test.ts
@@ -45,12 +45,16 @@ store.onStates(["count", "status"], change => {
store.setState("count", 1)
store.setState("status", "ready")
+const currentCount: number = store.getState("count")
+const currentStatus: "idle" | "ready" = store.getState("status")
const nextCount: number = store.dispatch("increment", 2)
const loaded: Promise = store.dispatch("load", "home")
// @ts-expect-error State keys must exist in the declared state object.
store.setState("missing", 1)
+// @ts-expect-error getState only accepts declared state keys.
+store.getState("missing")
// @ts-expect-error State values must match their declared key type.
store.setState("count", "1")
// @ts-expect-error Action arguments are inferred from the action signature.
diff --git a/index.d.ts b/index.d.ts
index 0f4f5ca..68aa542 100644
--- a/index.d.ts
+++ b/index.d.ts
@@ -99,6 +99,8 @@ export class HYEventStore<
stateValue: State[Key]
): void;
+ getState>(stateKey: Key): State[Key];
+
dispatch>(
actionName: ActionName,
...args: ActionArguments
diff --git a/src/event-store.js b/src/event-store.js
index be0822f..e131f8f 100644
--- a/src/event-store.js
+++ b/src/event-store.js
@@ -123,6 +123,13 @@ class HYEventStore {
this.state[stateKey] = stateValue
}
+ getState(stateKey) {
+ if (Object.keys(this.state).indexOf(stateKey) === -1) {
+ throw new Error("the state does not contain your key")
+ }
+ return this.state[stateKey]
+ }
+
dispatch(actionName, ...args) {
if (typeof actionName !== "string") {
throw new TypeError("the action name must be string type")