Movatterモバイル変換


[0]ホーム

URL:


Skip to content

Navigation Menu

Sign in
Appearance settings

Search code, repositories, users, issues, pull requests...

Provide feedback

We read every piece of feedback, and take your input very seriously.

Saved searches

Use saved searches to filter your results more quickly

Sign up
Appearance settings

RFC: decoupleuseEventCallback from the state management means #73

Open
Assignees
Brooooooklyn
@leoyli

Description

@leoyli

Hi, thank you for the inspiration and make this awesome libs. I have the following RFC proposed:

Suggestion

Remove the overlapping of state management inuseEventCallback, as we haveuseObservable,useState, anduseReducer already, making this hook just purely for the async event management. This hook is best to be treated as an adaptor to convert DOMEvent to DOMEvent stream.

Why

After using it for a while, I always thinkuseEventCallback is not so intuitive to use. It accepts up to 3 arguments:callback,initialState, andinputs. Why we need thisinitialState if we want to introduce prop dependencies? At the current implementation, what this hook would returns is not just a memorized event handler as a callback, but a tuple of handler + the observed state, which makes me feel this hook is doing too much, IMHO.

Ideally, what I have in mind is to have a similar interface as in the the nativeuseCallback. But what can make this hook so useful is to convert the event object to an observable. Then we can manage DOMEvent as a stream easily.

If we need to performance side effects, like to update state, we should usetap operator instead. For example:

constdeps=/* some dependencies, could be state, or props */const[state,setState]=useState();constonClick=useEventCallback((event$,deps$)=>event$.pipe(filter((e)=>e.type==='click'),map(({ target})=>target.value),tap((v)=>setState(v))// * re-render explicitly requested by the user.withLatestFrom(deps$),/* do something with dependencies */),[deps]// optional)

Thedeps would be any props or local state dependencies that is reactively emitted and passed as the second props to the callback. And we can think this like an epic inredux-observable. This way we decouple the state management part out of this hook. And this hook is never going to trigger re-render by itself, it will have to be introduced by the user.

Implementation

Here is the suggested implementation:

constcreateEvent$=<T>()=>{returnnewSubject<T>();};constcreateDeps$=<Dextendsunknown[]>(deps:D)=>{returndeps.length ?newBehaviorSubject(deps) :undefined;};exportconstuseEventCallback=<E=any,Dextendsunknown[]=never>(callback:(event$:Observable<E>,deps$:Observable<D>)=>Observable<unknown>,deps:D=[]asnever)=>{constevent$=useMemo(()=>createEvent$<E>(),[]);constdeps$=useMemo(()=>createDeps$<D>(deps),[]);useEffect(()=>{constsubscriber=callback(event$,deps$asObservable<D>).subscribe();return()=>{subscriber.unsubscribe();event$.complete();if(deps$){deps$.complete();}};},[]);useEffect(()=>{if(deps$){deps$.next(deps);}},deps);returnuseCallback((e:E)=>event$.next(e),[]);};

And perhaps, it should be called asuseEvent$Callback to make it more obvious what this hook is doing.

Metadata

Metadata

Assignees

Labels

No labels
No labels

Type

No type

Projects

No projects

Milestone

No milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions


    [8]ページ先頭

    ©2009-2025 Movatter.jp