A lightweight and easy-to-use React library for two-way data binding, simplifying state management and enhancing developer productivity.
react-binding-state!NPM Version
!NPM Downloads
!License
A lightweight, easy-to-use state management library for React that enables deep two-way data binding. It uses ES6 Proxies to automatically detect and update state changes, even in nested objects, without the need for manual setters or reducers.
Check out the live demo here: https://nmhung1210.github.io/react-binding-state
* Deep Two-Way Data Binding: Automatically syncs your UI with your state, even for deeply nested objects.
* Zero Boilerplate: No actions, reducers, or dispatchers needed. Just mutate the state directly.
* Lightweight: A small footprint, adding minimal overhead to your project.
* Intuitive API: A simple and straightforward API that is easy to learn and use.
* TypeScript Support: Written in TypeScript for a better developer experience with type safety and autocompletion.
``bash`
npm install react-binding-state
For the best experience, define a type for your global state.
`typescript`
// src/types.ts
export type AppState = {
user: {
name: string;
profile: {
email: string;
};
};
counter: number;
};
Provide the global state to your application by wrapping your root component with BindingStateProvider.
`tsx
// src/main.tsx
import React from 'react';
import ReactDOM from 'react-dom/client';
import App from './App';
import { BindingStateProvider } from 'react-binding-state';
import './App.css'; // Import global styles
import { AppState } from './types';
const initialState: AppState = {
user: {
name: 'John Doe',
profile: {
email: 'john.doe@example.com',
},
},
counter: 0,
};
ReactDOM.createRoot(document.getElementById('root')!).render(
);
`
Access and mutate the state from any component using the useBindingState hook. Changes are automatically reflected in your UI.
`tsx
// src/App.tsx
import React from 'react';
import { useBindingState } from 'react-binding-state';
import { AppState } from './types';
function UserProfile() {
const state = useBindingState
return (
Name: {state.user.name}
Email: {state.user.profile.email}
function Counter() {
const state = useBindingState
return (
Count: {state.counter}
function App() {
return (
export default App;
`
react-binding-state leverages a React Context to provide a global state object wrapped in an ES6 Proxy. When you modify a property on the state proxy (e.g., state.user.name = 'Jane Doe'), the proxy’s set trap is triggered. This trap efficiently updates the underlying state and triggers a re-render of only the components that use the modified state, ensuring your UI is always in sync with your data with minimal performance overhead.
This library is written in TypeScript to provide a first-class development experience.
* Type-Safe State: By providing a type to the useBindingState hook (e.g., useBindingState), you get full type safety and autocompletion for your state object.
* Reduced Bugs: Catch typos and other errors at compile time, not at runtime.
* Improved Readability: Type definitions make your code easier to understand and maintain.
Here's a summary of the benchmark results comparing BindingState with the native useState hook:
* CPU: AMD EPYC 7B12 (2 Cores)
* Memory: 8 GB
| Benchmark | BindingState (ops/sec) | useState (ops/sec) | Winner | Performance Gain |useState
| ---------------------------------- | ---------------------- | -------------------- | -------------- | ---------------- |
| Initial Render with Heavy State | 458.59 | 588.06 | | 1.28x |BindingState
| Heavy Simple State Update | 11,875.21 | 2,007.02 | | 5.92x |BindingState
| Heavy Deeply Nested State Update | 19,872.92 | 3,096.64 | | 6.42x |BindingState
| Heavy Array Item Update | 21,289.86 | 3,869.84 | | 5.50x |
The benchmark results highlight a clear trade-off:
* useState excels at initial rendering, offering a 1.28x performance advantage due to its native and highly optimized nature.
* BindingState is significantly faster for all types of state updates, with performance gains ranging from 5.50x to 6.42x. The most substantial improvements are seen in scenarios with deeply nested objects, where BindingState's direct mutation approach avoids the performance overhead of manual immutable updates.
In summary, for applications with complex state and frequent updates, BindingState provides a compelling combination of performance and developer experience. If initial render time is the highest priority, useState may be a better choice. However, for applications where the user experience is defined by frequent state changes, BindingState offers a significant performance advantage.
```
------------------|---------|----------|---------|---------|-------------------
File | % Stmts | % Branch | % Funcs | % Lines | Uncovered Line #s
------------------|---------|----------|---------|---------|-------------------
All files | 100 | 100 | 100 | 100 |
BindingState.tsx | 100 | 100 | 100 | 100 |
------------------|---------|----------|---------|---------|-------------------
This project is licensed under the MIT License. See the LICENSE file for details.