-
Notifications
You must be signed in to change notification settings - Fork 2
/
Copy pathindex.ts
113 lines (100 loc) · 3.21 KB
/
index.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
/**
* @file uncontrolled
* @author Cuttle Cong
* @date 2018/6/16
* @description
*/
import isComponentClass from '@rcp/util.iscompclass'
import displayName from '@rcp/util.displayname'
import createLogger from '@rcp/util.createlogger'
const logger = createLogger('@rcp/hoc.uncontrolled')
function getDefaultName(name: string = '') {
return 'default' + name[0].toUpperCase() + name.slice(1)
}
function normalizePropList(propList: Prop[]): StrictProp[] {
return propList.map(prop => {
if (typeof prop === 'string') {
return {
withDefault: true,
eq: defaultEq,
name: prop
}
}
return {
withDefault: true,
eq: defaultEq,
...prop
}
})
}
/**
* @typedef StrictProp
* @public
* @param name {string}
* @param [withDefault=true] {boolean} - Whether check `default{propKey}` firstly
* @param [eq=(a, b) => a === b] {Function} - Detect new value and old value is equal
*/
export type StrictProp = { name: string; withDefault?: boolean; eq?: (oldValue, newValue) => boolean }
/**
* @typedef Prop {string | StrictProp}
* @public
*/
export type Prop = string | StrictProp
const defaultEq = (a, b) => a === b
/**
*
* @public
* @param propList {Prop[]} eg. `['value']` / `[{ name: 'value', withDefault: false, eq: (a, b) => a === b }]`
* @return {Function} `(Component: React.ComponentClass) => React.ComponentClass`
*/
export default function uncontrolled(propList: Prop[] = []) {
let propArray = normalizePropList(propList)
return function uncontrolled(Component: React.ComponentClass): React.ComponentClass {
logger.invariant(isComponentClass(Component), `\`Component\` should be a react component class`)
if (!propList.length) {
return Component
}
class Uncontrolled extends Component {
static displayName = `Uncontrolled_${displayName(Component)}`
state: any
constructor(props) {
super(props)
this.state = this.state || {}
propArray.forEach(prop => {
const propName = prop.name
if (prop.withDefault) {
const defaultPropName = getDefaultName(propName)
this.state[propName] =
typeof this.props[propName] === 'undefined'
? typeof this.props[defaultPropName] === 'undefined'
? this.state[propName]
: this.props[defaultPropName]
: this.props[propName]
} else {
if (typeof this.props[propName] !== 'undefined') {
this.state[propName] = this.props[propName]
}
}
})
}
componentWillReceiveProps(newProps) {
if (super.componentWillReceiveProps) {
super.componentWillReceiveProps.apply(this, arguments)
}
const newState = {}
let hasNewRecord = false
propArray.forEach(prop => {
const { name: propName, eq = defaultEq } = prop
if (newProps && typeof newProps[propName] !== 'undefined' && !eq(this.state[propName], newProps[propName])) {
newState[propName] = newProps[propName]
hasNewRecord = true
}
})
if (hasNewRecord) {
this.setState(newState)
}
}
}
return Uncontrolled
}
}