Skip to content

Commit 79e92ce

Browse files
committed
feat: allow customized eq function
1 parent c2ca9e0 commit 79e92ce

File tree

3 files changed

+105
-30
lines changed

3 files changed

+105
-30
lines changed

packages/hoc.uncontrolled/__tests__/spec.test.tsx

Lines changed: 35 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -38,7 +38,7 @@ describe('hocUncontrolled', function() {
3838
})
3939

4040
it('should opt.withDefault', () => {
41-
@hocUncontrolled(['value'], { withDefault: false })
41+
@hocUncontrolled([{ name: 'value', withDefault: false }])
4242
class View extends React.Component {
4343
static defaultProps? = {}
4444
state: any
@@ -64,4 +64,38 @@ describe('hocUncontrolled', function() {
6464
})
6565
expect(wrapper.text()).toBe('xxx')
6666
})
67+
68+
it('should opt.eq', () => {
69+
@hocUncontrolled([{ name: 'value', eq: (a, b) => a.k === b.k }])
70+
class View extends React.Component {
71+
static defaultProps? = {}
72+
state: {
73+
value: any
74+
}
75+
76+
render() {
77+
return this.state.value.label || ''
78+
}
79+
}
80+
const AnyView = View as any
81+
82+
let wrapper = mount(<AnyView value={{ k: '1', label: 'a' }} />)
83+
expect(wrapper.text()).toBe('a')
84+
85+
// setValue failed
86+
wrapper.setProps({
87+
value: { k: '1', label: 'hahah' }
88+
})
89+
expect(wrapper.text()).toBe('a')
90+
expect((wrapper.state() as any).value).toEqual({ k: '1', label: 'a' })
91+
92+
wrapper.setProps({
93+
value: { k: '2', label: 'a' }
94+
})
95+
expect((wrapper.state() as any).value).toEqual({ k: '2', label: 'a' })
96+
wrapper.setProps({
97+
value: { k: '1', label: 'hahah' }
98+
})
99+
expect((wrapper.state() as any).value).toEqual({ k: '1', label: 'hahah' })
100+
})
67101
})

packages/hoc.uncontrolled/index.d.ts

Lines changed: 19 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -5,19 +5,29 @@
55
* @description
66
*/
77
/// <reference types="react" />
8+
/**
9+
* @typedef StrictProp
10+
* @public
11+
* @param name {string}
12+
* @param [withDefault=true] {boolean} - Whether check `default{propKey}` firstly
13+
* @param [eq=(a, b) => a === b] {Function} - Detect new value and old value is equal
14+
*/
15+
export declare type StrictProp = {
16+
name: string
17+
withDefault?: boolean
18+
eq?: (oldValue: any, newValue: any) => boolean
19+
}
20+
/**
21+
* @typedef Prop {string | StrictProp}
22+
* @public
23+
*/
24+
export declare type Prop = string | StrictProp
825
/**
926
*
1027
* @public
11-
* @param propList {string[]} eg. `['value']`
12-
* @param {{}} options
13-
* @param {boolean} [options.withDefault = true] - Whether check `default{propKey}` firstly
28+
* @param propList {Prop[]} eg. `['value']` / `[{ name: 'value', withDefault: false }]`
1429
* @return {Function} `(Component: React.ComponentClass) => React.ComponentClass`
1530
*/
1631
export default function uncontrolled(
17-
propList?: any[],
18-
{
19-
withDefault
20-
}?: {
21-
withDefault?: boolean
22-
}
32+
propList?: Prop[]
2333
): (Component: import('react').ComponentClass<{}, any>) => import('react').ComponentClass<{}, any>

packages/hoc.uncontrolled/index.ts

Lines changed: 51 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -15,19 +15,48 @@ function getDefaultName(name: string = '') {
1515
return 'default' + name[0].toUpperCase() + name.slice(1)
1616
}
1717

18+
function normalizePropList(propList: Prop[]): StrictProp[] {
19+
return propList.map(prop => {
20+
if (typeof prop === 'string') {
21+
return {
22+
withDefault: true,
23+
eq: defaultEq,
24+
name: prop
25+
}
26+
}
27+
return {
28+
withDefault: true,
29+
eq: defaultEq,
30+
...prop
31+
}
32+
})
33+
}
34+
35+
/**
36+
* @typedef StrictProp
37+
* @public
38+
* @param name {string}
39+
* @param [withDefault=true] {boolean} - Whether check `default{propKey}` firstly
40+
* @param [eq=(a, b) => a === b] {Function} - Detect new value and old value is equal
41+
*/
42+
export type StrictProp = { name: string; withDefault?: boolean; eq?: (oldValue, newValue) => boolean }
43+
44+
/**
45+
* @typedef Prop {string | StrictProp}
46+
* @public
47+
*/
48+
export type Prop = string | StrictProp
49+
50+
const defaultEq = (a, b) => a === b
51+
1852
/**
1953
*
2054
* @public
21-
* @param propList {string[]} eg. `['value']`
22-
* @param {{}} options
23-
* @param {boolean} [options.withDefault = true] - Whether check `default{propKey}` firstly
55+
* @param propList {Prop[]} eg. `['value']` / `[{ name: 'value', withDefault: false }]`
2456
* @return {Function} `(Component: React.ComponentClass) => React.ComponentClass`
2557
*/
26-
export default function uncontrolled(propList = [], { withDefault = true } = {}) {
27-
logger.invariant(
28-
Array.isArray(propList) && propList.every(x => typeof x === 'string'),
29-
`\`propList\` should be string[], but ${typeof propList}`
30-
)
58+
export default function uncontrolled(propList: Prop[] = []) {
59+
let propArray = normalizePropList(propList)
3160

3261
return function uncontrolled(Component: React.ComponentClass): React.ComponentClass {
3362
logger.invariant(isComponentClass(Component), `\`Component\` should be a react component class`)
@@ -42,18 +71,19 @@ export default function uncontrolled(propList = [], { withDefault = true } = {})
4271
constructor(props) {
4372
super(props)
4473
this.state = this.state || {}
45-
propList.forEach(prop => {
46-
if (withDefault) {
47-
const defaultPropName = getDefaultName(prop)
48-
this.state[prop] =
49-
typeof this.props[prop] === 'undefined'
74+
propArray.forEach(prop => {
75+
const propName = prop.name
76+
if (prop.withDefault) {
77+
const defaultPropName = getDefaultName(propName)
78+
this.state[propName] =
79+
typeof this.props[propName] === 'undefined'
5080
? typeof this.props[defaultPropName] === 'undefined'
51-
? this.state[prop]
81+
? this.state[propName]
5282
: this.props[defaultPropName]
53-
: this.props[prop]
83+
: this.props[propName]
5484
} else {
55-
if (typeof this.props[prop] !== 'undefined') {
56-
this.state[prop] = this.props[prop]
85+
if (typeof this.props[propName] !== 'undefined') {
86+
this.state[propName] = this.props[propName]
5787
}
5888
}
5989
})
@@ -66,9 +96,10 @@ export default function uncontrolled(propList = [], { withDefault = true } = {})
6696

6797
const newState = {}
6898
let hasNewRecord = false
69-
propList.forEach(prop => {
70-
if (typeof newProps[prop] !== 'undefined' && this.state[prop] !== newProps[prop]) {
71-
newState[prop] = newProps[prop]
99+
propArray.forEach(prop => {
100+
const { name: propName, eq = defaultEq } = prop
101+
if (typeof newProps[propName] !== 'undefined' && !eq(this.state[propName], newProps[propName])) {
102+
newState[propName] = newProps[propName]
72103
hasNewRecord = true
73104
}
74105
})

0 commit comments

Comments
 (0)