Skip to content

Commit fd83648

Browse files
committed
Apply changesets and update CHANGELOG [skip ci]
1 parent f4eb020 commit fd83648

File tree

1 file changed

+126
-77
lines changed

1 file changed

+126
-77
lines changed

lib/README.md

Lines changed: 126 additions & 77 deletions
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,9 @@
1010

1111
### **A Modern, Lightweight, and High-Performance State Management Library for React**
1212

13-
**Kosha** is a production-ready, minimalistic global state management solution for modern React applications. Weighing in at just **~450 bytes minzipped**, it’s built for developers who care about performance, clean APIs, and full React 18+ compatibility.
13+
**Kosha** is a production-ready, minimalistic global state management solution for modern React applications. At just **\~450 bytes minzipped**, it's optimized for performance-critical applications, full React 18+ support, and clean developer ergonomics.
14+
15+
Live demo: [https://kosha-six.vercel.app](https://kosha-six.vercel.app)
1416

1517
---
1618

@@ -22,6 +24,7 @@
2224
- [⚖️ Zustand Comparison](#️-why-choose-kosha-over-zustand)
2325
- [📁 Examples](#-examples)
2426
- [❓ FAQ](#-faq)
27+
- [🚧 Known Limitations](#-known-limitations)
2528
- [🤝 Contributing](#-contributing)
2629
- [📜 License](#-license)
2730

@@ -30,30 +33,40 @@
3033
## 🚀 Features
3134

3235
1. **Ultra-Lightweight**
33-
- Minzipped size: ~**420–571 bytes**, making it ideal for performance-critical applications.
3436

35-
2. **Zero Unnecessary Re-renders**
36-
- Based on React’s `useSyncExternalStore`, Kosha ensures components only re-render when selected state changes.
37-
- Example:
38-
```tsx
39-
const count = useKosha(state => state.count);
40-
```
37+
- Minzipped size: **\~420–460 bytes**, ideal for bundle-sensitive projects.
38+
39+
2. **Optimized by Default: Zero Unnecessary Re-renders**
40+
41+
- Uses `useSyncExternalStore` for subscription handling and memoization.
42+
- No need to manually optimize with `shallow` equality — Kosha uses `JSON.stringify` internally to compare selector outputs.
4143

4244
3. **Partial State Updates**
43-
- Update only what you needno need to manually spread previous state:
45+
46+
- Update only what you need without spreading old state:
47+
4448
```tsx
4549
set({ count });
4650
set(state => ({ count: state.count + 1 }));
4751
```
4852

49-
4. **Flexible Consumption API**
50-
- Select specific fields or use the entire store:
53+
4. **Concurrent Rendering Ready**
54+
55+
- Fully supports React 18+ and concurrent features.
56+
57+
5. **Flexible Consumption API**
58+
59+
- Consume whole store or optimized slices via selectors.
60+
5161
```tsx
52-
const { count, setCount } = useKosha();
62+
const count = useKosha(state => state.count); // optimized - re-renders only when count changes
63+
const { count, setCount } = useKosha(); // re-renders for every state change
5364
```
5465

55-
5. **Concurrent Rendering Ready**
56-
- Fully compatible with React 18+ and the concurrent features thanks to `useSyncExternalStore`.
66+
6. **Middleware Architecture (BYO Middleware)**
67+
68+
- Middleware support is built-in. While Kosha doesnt yet include a plugin ecosystem like Zustand, you can define and compose custom middlewares easily.
69+
- Includes working [persist middleware](https://github.com/mayank1513/kosha/blob/main/lib/src/middleware/persist.ts) example out-of-the-box
5770

5871
---
5972

@@ -65,13 +78,13 @@ Install using your preferred package manager:
6578
pnpm add kosha
6679
```
6780

68-
or
81+
**_or_**
6982

7083
```bash
7184
npm install kosha
7285
```
7386

74-
or
87+
**_or_**
7588

7689
```bash
7790
yarn add kosha
@@ -92,55 +105,36 @@ const useKosha = create(set => ({
92105
}));
93106
```
94107

95-
### 2. Consume the Store (No Selector)
108+
### 2. Consume the Store (Full Access)
96109

97110
```tsx
98-
const Counter = () => {
99-
const { count, increment } = useKosha();
100-
101-
return (
102-
<div>
103-
<p>Count: {count}</p>
104-
<button onClick={increment}>Increment</button>
105-
</div>
106-
);
107-
};
111+
const { count, increment } = useKosha();
108112
```
109113

110-
### 3. Use Selectors
114+
> This will trigger re-render even when count or increment are not changed but other states [part of the same store] change.
111115

112-
```tsx
113-
const Counter = () => {
114-
const count = useKosha(state => state.count);
115-
const increment = useKosha(state => state.increment);
116-
117-
return (
118-
<div>
119-
<p>Count: {count}</p>
120-
<button onClick={increment}>Increment</button>
121-
</div>
122-
);
123-
};
124-
```
116+
### 3. Select Specific State
125117

126-
### 4. Enable Direct Updates via `set`
118+
```tsx
119+
const count = useKosha(state => state.count);
120+
const increment = useKosha(state => state.increment);
127121
128-
Kosha no longer exposes `.set` by default. To update the state externally, expose `set` explicitly:
122+
// or use shorthand
123+
const { count, increment } = useKosha(({ count, increment }) => ({ count, increment }));
124+
```
129125

130-
```ts
131-
import { create } from "kosha";
126+
### 4. Enable External Set Access (Optional)
132127

128+
```tsx
133129
const useKosha = create(set => ({
134130
count: 0,
135131
increment: () => set(state => ({ count: state.count + 1 })),
136-
set, // Exposed manually
132+
set, // manually exposed
137133
}));
138134
```
139135

140136
### 5. Update Outside React
141137

142-
Update the store from anywhere (outside React) using `getState()`:
143-
144138
```ts
145139
useKosha.getState().increment();
146140
```
@@ -149,69 +143,124 @@ useKosha.getState().increment();
149143

150144
## ⚖️ Why Choose Kosha Over Zustand?
151145

152-
Kosha offers a streamlined alternative for projects where **performance, simplicity**, and **minimal bundle size** matter most.
146+
| Feature | Kosha | Zustand |
147+
| ------------------- | ------------------------- | --------------------------------- |
148+
| Size (minzipped) | \~450 bytes | \~0.6–2.5kB+ (depending on usage) |
149+
| Optimized Selectors |Built-in via stringify | ⚠️ Manual / shallow equality |
150+
| Partial Updates |||
151+
| Middleware Support | ✅ (custom) | ✅ (rich plugin ecosystem) |
152+
| Devtools | ❌ (custom only) ||
153+
| Plugin Ecosystem | 🚧 (in development) ||
153154

154-
| Feature | Kosha | Zustand |
155-
| -------------------------------- | ------------ | ------------------------------------------------ |
156-
| Size (minzipped) | \~420 bytes | \~1.11.5 kB |
157-
| Built-in Optimized Selectors || ⚠️ Needs shallow equality or manual optimization |
158-
| Partial Updates | ✅ (native) ||
159-
| React 18+ `useSyncExternalStore` |||
160-
| Learning Curve | Super simple | Simple |
155+
Kosha is perfect for apps that prioritize bundle size, simplicity, and predictable updates without the need for extensive tooling.
161156

162-
**Example (Selector-Based Optimization in Kosha):**
157+
---
158+
159+
## 📁 Examples
160+
161+
Explore the [`examples/`](https://github.com/react18-tools/kosha/tree/main/examples) directory for working codebases, including component and selector examples.
162+
163+
---
164+
165+
## ❓ FAQ
166+
167+
### 1. Is Kosha production-ready?
168+
169+
Yes. Kosha is small, stable, and well-tested.
170+
171+
### 2. Does Kosha support async logic?
172+
173+
Absolutely. Define `async` functions inside the store:
163174

164175
```tsx
165-
const fullName = useKosha(state => state.firstName + state.lastName);
176+
const useStore = create(set => ({
177+
fetchUser: async () => {
178+
const user = await fetch("/api/user").then(res => res.json());
179+
set({ user });
180+
},
181+
}));
166182
```
167183

168-
Zustand may still trigger re-renders here unless carefully optimized. See [Zustands official docs](https://github.com/pmndrs/zustand/blob/main/docs/guides/prevent-rerenders-with-use-shallow.md) for details.
184+
### 3. Does Kosha support middleware like Zustand?
169185

170-
---
186+
Yes. Middleware support is built-in. A working persist middleware is included. You can easily build your own or extend with logging, devtools, etc.
171187

172-
## 📁 Examples
188+
### 4. Can I use it with `Set`, `Map`, or `Date` objects?
173189

174-
You can find real-world usage examples inside the [`examples/`](https://github.com/react18-tools/kosha/tree/main/examples) directory of this repository.
190+
While `Date` serializes fine, **avoid storing `Set` or `Map`** directly in global state, since Kosha uses `JSON.stringify` to diff selector outputs. Use arrays or plain objects instead for best results.
191+
192+
### 5. Isnt JSON.stringify unreliable because key order might change?
193+
194+
Noin Kosha, youre comparing outputs of the same selector function across renders. Since the order of keys in JavaScript objects is preserved in deterministic function outputs, JSON.stringify remains stable and reliable in this context.
175195

176196
---
177197

178-
## FAQ
198+
## 🚧 Known Limitations
179199

180-
### 1. Is Kosha production-ready?
200+
Kosha is intentionally minimal by design — built to offer just what most React apps need, without bloat. That comes with a few tradeoffs:
201+
202+
### 🧠 Selector Comparison via `JSON.stringify`
203+
204+
Kosha uses `JSON.stringify` internally to compare selector outputs for change detection. This works extremely well for the majority of cases — even with moderately large or deeply nested selectors.
181205

182-
Yes! Kosha is stable, lightweight, and testedsuitable for production environments.
206+
However, there are a few caveats:
183207

184-
### 2. Does Kosha support async actions?
208+
- **Avoid non-serializable values** in selectors like `Set`, `Map`, `WeakMap`, or circular objects.
209+
- **Very large selector outputs** may incur performance costs during diffing.
185210

186-
Yes. Define asynchronous logic inside your store methods using `async/await` or promise chains.
211+
> To clarify:
212+
> ✅ It’s perfectly fine to have a large store or global state.
213+
> ⚠️ What matters is the **output of your selector**. If you’re selecting a large slice like `state => ({ a: state.a, ..., z: state.z })`, it’s more efficient to either:
214+
>
215+
> - Access the store directly without a selector (`useKosha()`), or
216+
> - Extract only the minimal fields you actually need.
187217
188-
### 3. What happens if I don't expose `set`?
218+
### 🔌 Plugin Ecosystem Still Growing
189219
190-
You wont be able to update the store from outside React context. Expose `set` explicitly if needed.
220+
Kosha includes full support for custom middleware and already ships with a working [persist middleware](https://github.com/mayank1513/kosha/blob/main/lib/src/middleware/persist.ts). However:
191221
192-
### 4. Does Kosha support React Server Components?
222+
- Built-in plugins like `devtools` are not yet included.
223+
- A community-driven plugin ecosystem is still in its early stages.
193224
194-
Kosha is designed for client-side state management. Server Component integration isnt a current goal, but discussions are welcome.
225+
> That said, the underlying architecture is solid and middleware-ready — you're free to build and compose your own middleware as needed.
195226
196227
---
197228
198229
## 🤝 Contributing
199230
200-
We welcome contributions from the community!
231+
We welcome contributions!
232+
233+
- 💬 Start a [discussion](https://github.com/react18-tools/kosha/discussions)
234+
- 🐛 Report bugs on the [issue tracker](https://github.com/react18-tools/kosha/issues)
235+
- 🧪 Submit PRs to improve or extend Kosha
236+
237+
Got an idea for a middleware plugin or improvement? We'd love to collaborate.
238+
239+
---
240+
241+
## 🧠 Internals & Caveats
242+
243+
### 🔍 Why use `JSON.stringify` for selector comparison?
244+
245+
Kosha compares previous and next selector outputs using `JSON.stringify` to prevent unnecessary re-renders. This approach has several benefits:
246+
247+
- It's **fast** and works for most serializable primitives and plain objects.
248+
- It ensures **deep comparison** out-of-the-box without manual equality functions.
249+
250+
### ⚠️ What about key order in objects?
201251
202-
* 💬 Start a [discussion](https://github.com/react18-tools/kosha/discussions)
203-
* 🐛 Report issues on the [Issue Tracker](https://github.com/react18-tools/kosha/issues)
204-
* 🧪 Submit PRs to improve or extend Kosha
252+
This is a common concern, but not an issue in Kosha:
205253
206-
> Have an idea for a feature or roadmap suggestion? Open a discussion and help shape Koshas future.
254+
> Kosha always compares results of the **same selector function**, so the key order is preserved unless your selector behaves non-deterministically (e.g., relies on `Object.keys()` or mutation).
255+
> As long as your selector returns a consistent structure, `JSON.stringify` comparison will be reliable.
207256
208257
---
209258
210259
## 📜 License
211260
212261
Kosha is licensed under the **MPL-2.0** license.
213262
214-
<img src="https://raw.githubusercontent.com/mayank1513/mayank1513/main/popper.png" style="height: 20px"/> Explore [our courses](https://mayank-chaudhari.vercel.app/courses) or [support development](https://github.com/sponsors/mayank1513).
263+
<img src="https://raw.githubusercontent.com/mayank1513/mayank1513/main/popper.png" style="height: 20px"/> Explore [my courses](https://mayank-chaudhari.vercel.app/courses) or [support development](https://github.com/sponsors/mayank1513).
215264
216265
---
217266

0 commit comments

Comments
 (0)