@@ -21,90 +21,104 @@ const STATES = {
2121  SUCCESS : 'success' , 
2222} ; 
2323
24- export  default  function  AsyncButton < T  extends  React . ElementType > ( { 
25-   as, 
26-   errorConfig, 
27-   onClick, 
28-   pendingConfig, 
29-   resetTimeout =  2000 , 
30-   successConfig, 
31-   ...otherProps 
32- } : AsyncButtonProps < T > )  { 
33-   const  [ buttonState ,  setButtonState ]  =  useState ( STATES . INIT ) ; 
34-   const  cancellablePromise  =  useRef < ReturnType < typeof  makeCancellable > > ( ) ; 
35-   const  timeout  =  useRef < ReturnType < typeof  setTimeout > > ( ) ; 
36- 
37-   useEffect ( 
38-     ( )  =>  ( )  =>  { 
39-       if  ( cancellablePromise . current )  { 
40-         cancellablePromise . current . cancel ( ) ; 
41-       } 
42-       clearTimeout ( timeout . current ) ; 
43-     } , 
44-     [ ] , 
45-   ) ; 
46- 
47-   const  onClickInternal  =  useCallback ( 
48-     ( event : React . MouseEvent )  =>  { 
49-       if  ( ! onClick )  { 
50-         return ; 
51-       } 
24+ const  AsyncButton  =  React . forwardRef ( 
25+   < T  extends  React . ElementType > ( 
26+     { 
27+       as, 
28+       errorConfig, 
29+       onClick, 
30+       pendingConfig, 
31+       resetTimeout =  2000 , 
32+       successConfig, 
33+       ...otherProps 
34+     } : AsyncButtonProps < T > , 
35+     ref : React . ForwardedRef < any > , 
36+   )  =>  { 
37+     const  [ buttonState ,  setButtonState ]  =  useState ( STATES . INIT ) ; 
38+     const  cancellablePromise  =  useRef < ReturnType < typeof  makeCancellable > > ( ) ; 
39+     const  timeout  =  useRef < ReturnType < typeof  setTimeout > > ( ) ; 
40+ 
41+     useEffect ( 
42+       ( )  =>  ( )  =>  { 
43+         if  ( cancellablePromise . current )  { 
44+           cancellablePromise . current . cancel ( ) ; 
45+         } 
46+         clearTimeout ( timeout . current ) ; 
47+       } , 
48+       [ ] , 
49+     ) ; 
50+ 
51+     const  onClickInternal  =  useCallback ( 
52+       ( event : React . MouseEvent )  =>  { 
53+         if  ( ! onClick )  { 
54+           return ; 
55+         } 
5256
53-       clearTimeout ( timeout . current ) ; 
54- 
55-       const  onSuccess  =  ( )  =>  { 
56-         setButtonState ( STATES . SUCCESS ) ; 
57-       } ; 
58- 
59-       const  onError  =  ( )  =>  { 
60-         setButtonState ( STATES . ERROR ) ; 
61-       } ; 
62- 
63-       const  finallyCallback  =  ( )  =>  { 
64-         timeout . current  =  setTimeout ( ( )  =>  { 
65-           setButtonState ( STATES . INIT ) ; 
66-         } ,  resetTimeout ) ; 
67-       } ; 
68- 
69-       try  { 
70-         const  result  =  onClick ( event ) ; 
71-         setButtonState ( STATES . PENDING ) ; 
72- 
73-         if  ( result  instanceof  Promise )  { 
74-           cancellablePromise . current  =  makeCancellable ( result ) ; 
75-           cancellablePromise . current . promise 
76-             . then ( onSuccess ) 
77-             . catch ( onError ) 
78-             . finally ( finallyCallback ) ; 
79-         }  else  { 
80-           onSuccess ( ) ; 
57+         clearTimeout ( timeout . current ) ; 
58+ 
59+         const  onSuccess  =  ( )  =>  { 
60+           setButtonState ( STATES . SUCCESS ) ; 
61+         } ; 
62+ 
63+         const  onError  =  ( )  =>  { 
64+           setButtonState ( STATES . ERROR ) ; 
65+         } ; 
66+ 
67+         const  finallyCallback  =  ( )  =>  { 
68+           timeout . current  =  setTimeout ( ( )  =>  { 
69+             setButtonState ( STATES . INIT ) ; 
70+           } ,  resetTimeout ) ; 
71+         } ; 
72+ 
73+         try  { 
74+           const  result  =  onClick ( event ) ; 
75+           setButtonState ( STATES . PENDING ) ; 
76+ 
77+           if  ( result  instanceof  Promise )  { 
78+             cancellablePromise . current  =  makeCancellable ( result ) ; 
79+             cancellablePromise . current . promise 
80+               . then ( onSuccess ) 
81+               . catch ( onError ) 
82+               . finally ( finallyCallback ) ; 
83+           }  else  { 
84+             onSuccess ( ) ; 
85+             finallyCallback ( ) ; 
86+           } 
87+         }  catch  ( error )  { 
88+           onError ( ) ; 
8189          finallyCallback ( ) ; 
8290        } 
83-       }  catch  ( error )  { 
84-         onError ( ) ; 
85-         finallyCallback ( ) ; 
91+       } , 
92+       [ onClick ,  resetTimeout ] , 
93+     ) ; 
94+ 
95+     const  Component  =  as  ||  'button' ; 
96+ 
97+     const  buttonConfig : Config < typeof  Component >  |  null  |  undefined  =  ( ( )  =>  { 
98+       switch  ( buttonState )  { 
99+         case  STATES . ERROR :
100+           return  errorConfig ; 
101+         case  STATES . PENDING :
102+           return  pendingConfig ; 
103+         case  STATES . SUCCESS :
104+           return  successConfig ; 
105+         default :
106+           return  null ; 
86107      } 
87-     } , 
88-     [ onClick ,  resetTimeout ] , 
89-   ) ; 
90- 
91-   const  Component  =  as  ||  'button' ; 
92- 
93-   const  buttonConfig : Config < typeof  Component >  |  null  |  undefined  =  ( ( )  =>  { 
94-     switch  ( buttonState )  { 
95-       case  STATES . ERROR :
96-         return  errorConfig ; 
97-       case  STATES . PENDING :
98-         return  pendingConfig ; 
99-       case  STATES . SUCCESS :
100-         return  successConfig ; 
101-       default :
102-         return  null ; 
103-     } 
104-   } ) ( ) ; 
105- 
106-   return  < Component  onClick = { onClick  ? onClickInternal  : null }  { ...otherProps }  { ...buttonConfig }  /> ; 
107- } 
108+     } ) ( ) ; 
109+ 
110+     return  ( 
111+       < Component 
112+         ref = { ref } 
113+         onClick = { onClick  ? onClickInternal  : null } 
114+         { ...otherProps } 
115+         { ...buttonConfig } 
116+       /> 
117+     ) ; 
118+   } , 
119+ ) ; 
120+ 
121+ AsyncButton . displayName  =  'AsyncButton' ; 
108122
109123const  configProps  =  { 
110124  children : PropTypes . node , 
@@ -122,3 +136,7 @@ AsyncButton.propTypes = {
122136  resetTimeout : PropTypes . number , 
123137  successConfig : isConfigObject , 
124138} ; 
139+ 
140+ export  default  AsyncButton  as  < T  extends  React . ElementType > ( 
141+   props : AsyncButtonProps < T >  &  {  ref ?: React . Ref < any >  } , 
142+ )  =>  React . ReactElement ; 
0 commit comments