Newtk is a lightweight, hooks-based state management library built for modern React. Inspired by Zustand’s simplicity, Newtk helps you manage global state in functional React apps with minimal boilerplate — without reducers, actions, or context wrappers.
npm install newtk
- Extremely lightweight (~2KB gzipped)
- Built with native React hooks
- Simple API — one function to create global store
- No context, no reducers, no extra ceremony
- Fine-grained subscriptions with selectors
- Inspired by Zustand (but even simpler)
// store.js
import createStore from 'newtk';
const [useNewtkStore] = createStore((set, get) => ({
counter: 0,
increment: () => set(state => ({ counter: state.counter + 1 })),
decrement: () => set(state => ({ counter: state.counter - 1 })),
reset: () => set({ counter: 0 }),
}));
export default useNewtkStore;
// App.jsx
import React from 'react';
import useNewtkStore from './store';
export default function App() {
const counter = useNewtkStore(state => state.counter);
const increment = useNewtkStore(state => state.increment);
const decrement = useNewtkStore(state => state.decrement);
const reset = useNewtkStore(state => state.reset);
return (
<div style={{ textAlign: 'center', marginTop: 50 }}>
<h1>Count: {counter}</h1>
<button onClick={increment}>➕</button>
<button onClick={decrement} style={{ margin: '0 10px' }}>➖</button>
<button onClick={reset}>🔁</button>
</div>
);
}
const [useNewtkStore, api] = createStore(setupFn);
-
useNewtkStore(selector)
– React Hook to select and subscribe to slices of state -
api.subscribe(fn)
– Subscribe manually (outside React) -
api.accessState()
– Get the current global state -
api.reset()
– Reset state and clear listeners
-
Introduced
generate()
function to create global state -
Support for:
-
set()
to update state -
get()
to access state -
useStore(selector)
hook to subscribe - Basic
subscribe()
/destroy()
-
-
No use of
useRef
, selector fallback directly returns entire state.
Example:
const [useStore] = generate((set, get) => ({
data: 0,
update: () => set({ data: 42 }),
}));
-
Introduced
useRef
(liveChunk
) to avoid stale closures in effects - Selector fallback now spreads state (
{...state}
) to ensure fresh shallow copy - More robust shallow equality check to avoid unnecessary re-renders
- Ensures updates are only triggered if the actual selected slice changes
Why this matters:
Without useRef
, stale values may cause missed updates in components due to outdated closures. Now, we always compare against the latest selected value in memory.
In v1.0.2
, we moved subscriptions
into the globalStore
object to:
- Keep state and listeners tightly encapsulated together.
-
Avoid leaking scope variables like
listeners
across renders. - Improve future maintainability and possible multi-store support.
let listeners = []
let state = { current: ... }
let globalStore = {
subscriptions: [],
current: ...
}
This change is internal but improves structure significantly without affecting the external API.
- Improved internal comparison using
shallowEqual()
to ensure components re-render only when relevant state changes, not due to new object references. - Result: Better performance and predictable render behavior.
- You can now update the store from anywhere — outside of React components — using:
storeAPI.update({ key: value }); // or storeAPI.update(prev => ({ count: prev.count + 1 }));
- Use selectors like
state => state.count
to optimize rendering - Prefer updating partial state via
set(prev => ({ ... }))
- You can destructure multiple slices like:
const { user, logout } = useStore(state => ({
user: state.user,
logout: state.logout,
}));
MIT © Kavya Katal