@bangcao2020/reactsync
v1.0.6
Published
Component to component data synchronization library for React applications.
Downloads
725
Readme
Author: Bang Cao.
linkedin.com/in/bangcao24
Lisense: MIT
Why ReactSync?
Managing state and data synchronization in React can be messy:
- Context often causes unnecessary re-renders.
- Global stores require boilerplate setup.
- Prop drilling or ad-hoc event buses get complicated at scale.
ReactSync solves this:
- Only the components that need updates re-render.
- Automatic lifecycle handling — mount, unmount, sync.
- Declarative component wiring with zero boilerplate.
Demos
- https://github.com/bangcaodinh2020-droid/Miniblog-ReactSync
- https://github.com/bangcaodinh2020-droid/ECommercialApp-ReactSync
- https://github.com/bangcaodinh2020-droid/Demo-TableComponent-With-ReactSync
Features
- Plug & Play: Just two components needed: and your child components.
- Targeted Updates: Only components subscribed to a key update.
- Declarative Sync: Components declare which state they sync with.
- Lifecycle Safe: Auto-registration and cleanup on mount/unmount.
- High DX: Zero-store, no boilerplate, no manual subscriptions.
- Performance Ready: Designed for 1,000+ components with minimal overhead.
Advanced Usage / API
- Add multiple components dynamically — auto-registration handled.
- Remove components — auto cleanup, no orphan subscriptions.
- Access App data from any child component directly.
Performance
- Tested with 5,000+ components
- Targeted updates prevent broadcast re-renders
- Minimal memory overhead
- Works smoothly even with dynamic mount/unmount cycles
Installation
npm install @bangcao2020/reactsync
or
yarn add @bangcao2020/reactsync
////////////////
## 💡 Quick Start
///////////////
Example 1:
///////////////
We can use this ReactSync without the central store.
import {BaseApp, Test3} from "@bangcao2020/reactsync";
export function App() {
return (
<div>
<Test3 channel={"count"} defaultValue={5}> A </Test3>
<Test3 channel={"count"} defaultValue={60}> B </Test3>
</div>
)
}
Then at another place, we use the app as below.
<App/>
////////////////
Example 2:
////////////////
We can use this ReactSync without the central store.
import {BaseApp, Test4} from "@bangcao2020/reactsync";
export function App() {
return (
<div>
<Test4 > C </Test4>
<Test4 > D </Test4>
</div>
)
}
Then at another place, we use the app as below.
<App/>
///////////////
Example 3:
///////////////
We can use this ReactSync without the central store.
import {BaseApp, Test4} from "@bangcao2020/reactsync";
export function App() {
return (
<div>
<Test3 channel={"count"} defaultValue={5}> A </Test3>
<Test3 channel={"count"} defaultValue={60}> B </Test3>
<Test4 > C </Test4>
<Test4 > D </Test4>
</div>
)
}
Then at another place, we use the app as below.
<App/>
///////////////
Example 4:
///////////////
OR we can use an app as central store.
export class TestApp extends BaseApp {
render(){
return (
<div>
<Test3 channel={"count"} defaultValue={5}> A </Test3>
<Test3 channel={"count"} defaultValue={60}> B </Test3>
<Test4 > C </Test4>
<Test4 > D </Test4>
<Test5 id='E' data={{syncers:["F"]}}> E</Test5>
<Test5 id='F' data={{syncers:["E"]}}> F</Test5>
<Test6 id='k' data={{syncers:["l"]}}> K</Test6>
<Test6 id='l' data={{syncers:["k"]}}> L</Test6>
</div>
);
}
}
Then at another place ( main, or index), we use the app as below.
<TestApp id="testApp" data={{syncData:{count:10}}}></TestApp>
- syncData, play a role as store.
<TestApp id="testApp" data={{syncData:{count:10, tableData:{}}}}></TestApp>
- or empty store
<TestApp id="testApp" data={{syncData:{}}}></TestApp>
///////////////////////
Example 5:
///////////////////////
<BaseApp id="baseApp" data={{syncData:{count:10}}}>
<Test3 channel={"count"} defaultValue={5}> A </Test3>
<Test3 channel={"count"} defaultValue={60}> B </Test3>
<Test4 > C </Test4>
<Test4 > D </Test4>
<Test5 id='E' data={{syncers:["F"]}}> E</Test5>
<Test5 id='F' data={{syncers:["E"]}}> F</Test5>
<Test6 id='k' data={{syncers:["l"]}}> K</Test6>
<Test6 id='l' data={{syncers:["k"]}}> L</Test6>
</BaseApp>
///////////////////////////////////////////////////
Example 6: Customize your component with useChannel
///////////////////////////////////////////////////
import {useChannel} from "@bangcao2020/reactsync";
export const CustomComponent = (props: any) => {
const [count, setCount] = useChannel(props.channel, props.defaultValue, props);
const onClick = () => {
setCount(count + 1);
};
return (
<div onClick={onClick}>
Click Me {props.children} ({count})
</div>
);
};
then use it
<CustomComponent channel={"countChannel"} defaultValue={5}> A </CustomComponent>
<CustomComponent channel={"countChannel"} defaultValue={50}> B </CustomComponent>
///////////////////////
Example 6: Or like this.
///////////////////////
export const AnotherComponent = (props: any) => {
const [xyz, setXyz] = useChannel("abc", 3, props);
const onClick = () => {
setXyz(xyz + 1);
};
return (
<div onClick={onClick}>
Click Me {props.children} ({xyz})
</div>
);
};
Then use it
<AnotherComponent> A</AnotherComponent>
<AnotherComponent> B</AnotherComponent>
///////////////////////////////////////
If you want to use Id and central store.
Example 8:
////////////////////////////////////////
import type {BaseProps} from "@bangcao2020/reactsync";
export const ComponentA=(props: BaseProps)=>
{
const syncer = useRef(null);
const [count, setCount] = useState(0);
useEffect(()=>{
if (syncer.current) return;
syncer.current = new Syncer(props);
syncer.current.onNotified = onNotified;
}, []);
const onClick=()=>{
//send message, data to another component
syncer.current.sendMessage("Hello", {count: count+1});
setCount(count + 1);
}
const onNotified = (event: any)=>{
//Get message, data from other components
setCount(event.data.count);
}
return (<div onClick={onClick}>
Click Me {props.children} ({count})
</div>);
}
Then you can use it.
<BaseApp id="baseApp" data={{syncData:{count:10}}}/>
<ComponentA id='E' data={{syncers:["F"]}}> E</ComponentA>
<ComponentA id='F' data={{syncers:["E"]}}> F</ComponentA>
///////////////////////////////////
Example 9: you can use getSyncer
//////////////////////////////////
export const ComponentB=(props: BaseProps)=>
{
const onNotified = (event: any)=>{
// where you get the update, message from other components
setCount(event.data.count);
}
const syncer = useRef(null);
const [count, setCount] = useState(0);
const onClick=()=>{
//call sendMessage anytime you need to send data to another components
//
if(syncer.current)
syncer.current.sendMessage("Hello", {count: count+1});
setCount(count + 1);
}
useEffect(()=>{
if (syncer.current) return;
syncer.current = getSyncer(props, onNotified);
}, []);
return (<div onClick={onClick}>
Click Me {props.children} ({count})
</div>);
}
Then you use it somewhere else.
<BaseApp id="baseApp" data={{syncData:{count:10}}}/>
<ComponentB id='l' data={{syncers:["k"]}}> L</ComponentB>
<ComponentB id='k' data={{syncers:["l"]}}> L</ComponentB>
////////////////////////////////////////////////////
Example 10: work with table.
///////////////////////////////////////////////////
import { App, TableComponent } from "@bangcao2020/reactsync";
function Dashboard() {
return (
<App data={{ syncData: { tableData: "initial value" } }}>
<TableComponent
id="table1"
data={{ tableName: "tableData", syncers: ["view1", "view2"] }}
>
My Table
</TableComponent>
</App>
);
}
Explanation:
id → Unique identifier for the component
tableName → Key in App’s syncData
syncers → Other components to notify when data changes
⚡ Advanced Usage
import { App, TableComponent } from "@bangcao2020/reactsync";
function Dashboard() {
return (
<App data={{ syncData: { tableData: [], userData: {} } }}>
<TableComponent
id="tableA"
data={{ tableName: "tableData", syncers: ["tableB", "tableC"] }}
>
Table A
</TableComponent>
<TableComponent
id="tableB"
data={{ tableName: "tableData", syncers: ["tableA"] }}
>
Table B
</TableComponent>
<TableComponent
id="tableC"
data={{ tableName: "userData", syncers: ["tableA"] }}
>
Table C
</TableComponent>
</App>
);
}
Example:
import BaseComponent, {ButtonComponent,
MessageType,
TestComponent,
TableComponent,
CounterComponent,
ViewRowComponent,
PaginationComponent
} from "@bangcao2020/reactsync";
---
## 📊 Usage
### Example 1- View, delete a row.
<App id="appLayout" data={{ syncData:{
mode:"production",
tableData:{rows:AddRow(10), columns: columns},
table2:{rows: rows2, columns: columns2},
}}}/>
### Then you can use like this.
<ViewRowComponent id="viewRow1"
data={{fromTableName:"tableData",
fields:["Col 1", "Col 2", "Col 3"],
styles:{}
}}>View 1</ViewRowComponent>
<TableComponent id="testTable"
data={{tableName:"tableData",
tableTarget:"table2",
actions:[{label:"Delete", type:"deleteARow"}, {label:"View", type:"viewARow"},],
syncers:["viewRow1", "viewRow2", "testTable2"],
}}> My Table</TableComponent>
---
### Example 2 - Customize table, pagination
---
<TableComponent id="testTable11"
data={{tableName:"tableData",
tableTarget:"table2",
syncers:["viewRow11", "viewRow12", "testTable12"],
actions:[{label:"Delete", type:"deleteARow"}, {label:"View", type:"viewARow"}],
styles:{table:"table table-info table-hover", tbody:"", tr:"", th:"bg-primary", td:"", button:"rounded bg-primary border border-0 mx-1" },
}}> My Table</TableComponent>
<PaginationComponent id={"pagination11"}
data={{rowsPerPage:10,
numOfPageNode:4,
tableName:"tableData",
syncers:["testTable11"],
styles:{button:"page-link text-info"}}}></PaginationComponent>
### Example 3 - Move data between tables
<TableComponent id="testTable11"
data={{tableName:"tableData",
tableTarget:"table2",
syncers:["viewRow11", "viewRow12", "testTable12"],
actions:[{label:"Delete", type:"deleteARow"}, {label:"View", type:"viewARow"}],
styles:{table:"table table-info table-hover", tbody:"", tr:"", th:"bg-primary", td:"", button:"rounded bg-primary border border-0 mx-1" },
}}> My Table</TableComponent>
<PaginationComponent id={"pagination11"}
data={{rowsPerPage:10,
numOfPageNode:4,
tableName:"tableData",
syncers:["testTable11"],
styles:{button:"page-link text-info"}}}></PaginationComponent>
### Example 4 - Edit row data
<EditRowComponent id="viewRow32"
data={{
styles:{button:"bg-success rounded"},
}}>View Edit</EditRowComponent>
<TableComponent id="testTable31"
data={{tableName:"tableData",
tableTarget:"table2",
syncers:["viewRow31", "viewRow32", "testTable32"],
actions:actions,
styles:styles,
}}> My Table</TableComponent>
