Skip to content

Commit

Permalink
feat: add ctrl.flushSync for opt-out batch-refresh
Browse files Browse the repository at this point in the history
  • Loading branch information
Lucifier129 committed Jun 26, 2024
1 parent 09c9b12 commit d93dd15
Show file tree
Hide file tree
Showing 4 changed files with 82 additions and 32 deletions.
46 changes: 34 additions & 12 deletions controller/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -638,6 +638,24 @@ export default class Controller {
return this.render()
}
disableBatchRefresh = true

/**
* 同步触发 store 的 subscribe 回调,刷新视图
* 类似于 react 18 的 flushSync
*/
flushSync(fn) {
if (this.disableBatchRefresh) {
fn()
return
}
this.disableBatchRefresh = true
try {
fn()
} finally {
this.disableBatchRefresh = false
}
}

bindStoreWithView() {
let { context, store, history, meta } = this

Expand All @@ -647,19 +665,23 @@ export default class Controller {
}

if (store) {
let refresh = (data) => {
if (meta.isDestroyed) return
this.refreshView && this.refreshView()
if (this.stateDidChange) {
this.stateDidChange(data)
}
}

if (!this.disableBatchRefresh) {
refresh = _.debounce(refresh, 5)
}
let refresh = _.debounce(
((data) => {
if (meta.isDestroyed) return
this.refreshView && this.refreshView()
if (this.stateDidChange) {
this.stateDidChange(data)
}
}),
5
)

let unsubscribe = store.subscribe(refresh)
let unsubscribe = store.subscribe(data => {
refresh(data)
if (this.disableBatchRefresh) {
refresh.flush()
}
})
meta.unsubscribeList.push(unsubscribe)
}

Expand Down
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "react-imvc",
"version": "2.10.23",
"version": "2.10.24",
"description": "An Isomorphic MVC Framework",
"main": "./index",
"bin": {
Expand Down
48 changes: 33 additions & 15 deletions project/src/batch-refresh/Controller.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -24,10 +24,11 @@ export default class BatchRefreshController extends Controller {
}
}
initialState = {
count: 0
count: 0,
text: ''
}
// 选择是否禁用防抖刷新
disableBatchRefresh: boolean = true
disableBatchRefresh: boolean = false
handleIncre = () => {
setTimeout(() => {
// 重复调用多次,只会触发一次更新
Expand All @@ -39,31 +40,48 @@ export default class BatchRefreshController extends Controller {
})
}, 0)
}
}


function View({ state }: any) {
const ctrl = useCtrl()
const [count, setCount] = useState(0)

const handleIncre = () => {
handleIncreFlushSync = () => {
setTimeout(() => {
setCount(count => count + 1)
setCount(count => count + 1)
this.flushSync(() => {
this.store.actions.UPDATE_STATE({
count: this.store.getState().count + 1
})
this.store.actions.UPDATE_STATE({
count: this.store.getState().count + 1
})
})
}, 0)

}

handleChange = (text: string) => {
this.flushSync(() => {
this.store.actions.UPDATE_STATE({
text: text
})
})
}

}

function View({ state }: any) {
const ctrl = useCtrl()

console.log('render', state.count, count)
console.log('render', state.count, state.text)
return (
<div id="debounce-refresh">
<div id="count">{state.count}</div>
<button id="ctrl-incre" onClick={ctrl.handleIncre}>
ctrl.incre
batch-incre
</button>
<button id="state-incre" onClick={handleIncre}>
state.incre
<button id="state-incre" onClick={ctrl.handleIncreFlushSync}>
flush-incre
</button>
<div>
<label>flush-input</label>
<input id="text" value={state.text} onChange={(e) => ctrl.handleChange(e.target.value)} />
</div>
</div>
)
}
18 changes: 14 additions & 4 deletions util/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -124,12 +124,22 @@ function getValueByPath(obj: objectOrArray, path: string | string[]) {
return getPath(path).reduce(getValue, obj)
}

function debounce<T>(func: (data: T) => unknown, wait: number): typeof func {
function debounce<T>(func: (data: T) => unknown, wait: number) {
let timeout: ReturnType<typeof setTimeout>
return function (data: T) {
let currentFn: (() => void) | undefined
const flush = () => {
clearTimeout(timeout)
timeout = setTimeout(() => {
currentFn?.()
currentFn = undefined
}
const debouncedFn = function (data: T) {
currentFn = () => {
func(data)
}, wait)
}
timeout = setTimeout(flush, wait)
}

debouncedFn.flush = flush

return debouncedFn
}

0 comments on commit d93dd15

Please sign in to comment.