Skip to content

Commit d93dd15

Browse files
committed
feat: add ctrl.flushSync for opt-out batch-refresh
1 parent 09c9b12 commit d93dd15

File tree

4 files changed

+82
-32
lines changed

4 files changed

+82
-32
lines changed

controller/index.js

Lines changed: 34 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -638,6 +638,24 @@ export default class Controller {
638638
return this.render()
639639
}
640640
disableBatchRefresh = true
641+
642+
/**
643+
* 同步触发 store 的 subscribe 回调,刷新视图
644+
* 类似于 react 18 的 flushSync
645+
*/
646+
flushSync(fn) {
647+
if (this.disableBatchRefresh) {
648+
fn()
649+
return
650+
}
651+
this.disableBatchRefresh = true
652+
try {
653+
fn()
654+
} finally {
655+
this.disableBatchRefresh = false
656+
}
657+
}
658+
641659
bindStoreWithView() {
642660
let { context, store, history, meta } = this
643661

@@ -647,19 +665,23 @@ export default class Controller {
647665
}
648666

649667
if (store) {
650-
let refresh = (data) => {
651-
if (meta.isDestroyed) return
652-
this.refreshView && this.refreshView()
653-
if (this.stateDidChange) {
654-
this.stateDidChange(data)
655-
}
656-
}
657-
658-
if (!this.disableBatchRefresh) {
659-
refresh = _.debounce(refresh, 5)
660-
}
668+
let refresh = _.debounce(
669+
((data) => {
670+
if (meta.isDestroyed) return
671+
this.refreshView && this.refreshView()
672+
if (this.stateDidChange) {
673+
this.stateDidChange(data)
674+
}
675+
}),
676+
5
677+
)
661678

662-
let unsubscribe = store.subscribe(refresh)
679+
let unsubscribe = store.subscribe(data => {
680+
refresh(data)
681+
if (this.disableBatchRefresh) {
682+
refresh.flush()
683+
}
684+
})
663685
meta.unsubscribeList.push(unsubscribe)
664686
}
665687

package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
{
22
"name": "react-imvc",
3-
"version": "2.10.23",
3+
"version": "2.10.24",
44
"description": "An Isomorphic MVC Framework",
55
"main": "./index",
66
"bin": {

project/src/batch-refresh/Controller.tsx

Lines changed: 33 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -24,10 +24,11 @@ export default class BatchRefreshController extends Controller {
2424
}
2525
}
2626
initialState = {
27-
count: 0
27+
count: 0,
28+
text: ''
2829
}
2930
// 选择是否禁用防抖刷新
30-
disableBatchRefresh: boolean = true
31+
disableBatchRefresh: boolean = false
3132
handleIncre = () => {
3233
setTimeout(() => {
3334
// 重复调用多次,只会触发一次更新
@@ -39,31 +40,48 @@ export default class BatchRefreshController extends Controller {
3940
})
4041
}, 0)
4142
}
42-
}
43-
44-
45-
function View({ state }: any) {
46-
const ctrl = useCtrl()
47-
const [count, setCount] = useState(0)
4843

49-
const handleIncre = () => {
44+
handleIncreFlushSync = () => {
5045
setTimeout(() => {
51-
setCount(count => count + 1)
52-
setCount(count => count + 1)
46+
this.flushSync(() => {
47+
this.store.actions.UPDATE_STATE({
48+
count: this.store.getState().count + 1
49+
})
50+
this.store.actions.UPDATE_STATE({
51+
count: this.store.getState().count + 1
52+
})
53+
})
5354
}, 0)
55+
5456
}
5557

58+
handleChange = (text: string) => {
59+
this.flushSync(() => {
60+
this.store.actions.UPDATE_STATE({
61+
text: text
62+
})
63+
})
64+
}
65+
66+
}
67+
68+
function View({ state }: any) {
69+
const ctrl = useCtrl()
5670

57-
console.log('render', state.count, count)
71+
console.log('render', state.count, state.text)
5872
return (
5973
<div id="debounce-refresh">
6074
<div id="count">{state.count}</div>
6175
<button id="ctrl-incre" onClick={ctrl.handleIncre}>
62-
ctrl.incre
76+
batch-incre
6377
</button>
64-
<button id="state-incre" onClick={handleIncre}>
65-
state.incre
78+
<button id="state-incre" onClick={ctrl.handleIncreFlushSync}>
79+
flush-incre
6680
</button>
81+
<div>
82+
<label>flush-input</label>
83+
<input id="text" value={state.text} onChange={(e) => ctrl.handleChange(e.target.value)} />
84+
</div>
6785
</div>
6886
)
6987
}

util/index.ts

Lines changed: 14 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -124,12 +124,22 @@ function getValueByPath(obj: objectOrArray, path: string | string[]) {
124124
return getPath(path).reduce(getValue, obj)
125125
}
126126

127-
function debounce<T>(func: (data: T) => unknown, wait: number): typeof func {
127+
function debounce<T>(func: (data: T) => unknown, wait: number) {
128128
let timeout: ReturnType<typeof setTimeout>
129-
return function (data: T) {
129+
let currentFn: (() => void) | undefined
130+
const flush = () => {
130131
clearTimeout(timeout)
131-
timeout = setTimeout(() => {
132+
currentFn?.()
133+
currentFn = undefined
134+
}
135+
const debouncedFn = function (data: T) {
136+
currentFn = () => {
132137
func(data)
133-
}, wait)
138+
}
139+
timeout = setTimeout(flush, wait)
134140
}
141+
142+
debouncedFn.flush = flush
143+
144+
return debouncedFn
135145
}

0 commit comments

Comments
 (0)