forked from gaperton/React-MVx
-
Notifications
You must be signed in to change notification settings - Fork 3
/
component.ts
147 lines (120 loc) · 4.74 KB
/
component.ts
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
/**
* React-Type-R component base class. Overrides React component.
*/
import * as React from 'react';
import { EventCallbacks, Messenger, Record, Store, define, definitions, mixinRules, mixins } from 'type-r';
import onDefine, { EmptyPropsChangeTokensCtor, TypeSpecs } from './define';
import Link from './link';
@define({
PropsChangeTokens : EmptyPropsChangeTokensCtor
})
@definitions({
// Definitions to be extracted from mixins and statics and passed to `onDefine()`
state : mixinRules.merge,
State : mixinRules.value,
store : mixinRules.merge,
Store : mixinRules.value,
props : mixinRules.merge,
context : mixinRules.merge,
childContext : mixinRules.merge,
pureRender : mixinRules.protoValue
})
@mixinRules( {
// Apply old-school React mixin rules.
componentWillMount : mixinRules.classLast,
componentDidMount : mixinRules.classLast,
componentWillReceiveProps : mixinRules.classLast,
componentWillUpdate : mixinRules.classLast,
componentDidUpdate : mixinRules.classLast,
componentWillUnmount : mixinRules.classFirst,
// And a bit more to fix inheritance quirks.
shouldComponentUpdate : mixinRules.some,
getChildContext : mixinRules.defaults
} )
// Component can send and receive events...
@mixins( Messenger )
export class Component<P, S extends Record = Record > extends React.Component<P, S> {
cid : string
static state? : TypeSpecs | typeof Record
static store? : TypeSpecs | typeof Store
static props? : TypeSpecs
static context? : TypeSpecs
static childContext? : TypeSpecs
static pureRender? : boolean
private static propTypes: any;
private static defaultProps: any;
private static contextTypes : any;
private static childContextTypes : any;
private PropsChangeTokens : Function
static extend : ( spec : object, statics? : object ) => Component< any >
linkAt( key : string ) : Link<any> {
// Quick and dirty hack to suppres type error - refactor later.
return ( this.state as any ).linkAt( key );
}
linkAll( ...keys : string[] ) : { [ key : string ] : Link<any> }
linkAll(){
// Quick and dirty hack to suppres type error - refactor later.
const { state } = this as any;
return state.linkAll.apply( state, arguments );
}
linkPath( path : string ) : Link<any> {
return ( this.state as any ).linkPath( path );
}
get links(){
return ( this.state as any )._links;
}
static onDefine = onDefine;
readonly state : S
readonly store? : Store
constructor( props?, context? ){
super( props, context );
this._initializeState();
}
_initializeState(){
( this as any ).state = null;
}
assignToState( x, key : string ){
this.state.assignFrom({ [ key ] : x });
}
setState( attrs : object | ( ( state : S, props : P ) => object ) ){
this.state.set( typeof attrs === 'function' ? attrs.call( this, this.state, this.props ) : attrs );
}
isMounted : () => boolean
componentWillUnmount(){
this.dispose();
}
/**
* Performs transactional update for both props and state.
* Suppress updates during the transaction, and force update aftewards.
* Wrapping the sequence of changes in a transactions guarantees that
* React component will be updated _after_ all the changes to the
* both props and local state are applied.
*/
transaction( fun : ( state? : Record ) => void ){
var shouldComponentUpdate = this.shouldComponentUpdate,
isRoot = shouldComponentUpdate !== returnFalse;
if( isRoot ){
this.shouldComponentUpdate = returnFalse;
}
const { state, store } = this,
withStore = store ? state => store.transaction( () => fun( state ) ) : fun;
state ? state.transaction( withStore ) : withStore( state );
if( isRoot ){
this.shouldComponentUpdate = shouldComponentUpdate;
this.asyncUpdate();
}
}
// Safe version of the forceUpdate suitable for asynchronous callbacks.
asyncUpdate(){
this.shouldComponentUpdate === returnFalse || this._disposed || this.forceUpdate();
}
}
export interface Component<P, S extends Record = Record> extends Messenger {}
function returnFalse(){ return false; }
// Looks like React guys _really_ want to deprecate it. But no way.
// We will work around their attempt.
Object.defineProperty( Component.prototype, 'isMounted', {
value : function isMounted(){
return !this._disposed;
}
})