Skip to content
This repository was archived by the owner on Feb 15, 2019. It is now read-only.

Commit 9d44c45

Browse files
committed
chore: use "render props" instead of HOC
BREAKING CHANGE: use "render props" instead of HOC
1 parent 4fe521e commit 9d44c45

5 files changed

+44
-72
lines changed

Diff for: src/UniformComponent.test.tsx

+4-16
Original file line numberDiff line numberDiff line change
@@ -1,31 +1,19 @@
11
// tslint:disable:jsx-no-lambda
22
import * as React from "react"
33
import { mount } from "enzyme"
4-
import { UniformComponent, UniformProps } from "./index"
4+
import { UniformComponent } from "./index"
55

66
describe("<UniformComponent>", () => {
7-
class TestUniformComponent extends React.Component<
8-
UniformProps<{ bar: string }> & { asdf?: string }
9-
> {
10-
render() {
11-
return <input onChange={ev => this.props.data.change.bar(ev.target.value)} type="string" />
12-
}
13-
}
14-
const UniformTestComponent = UniformComponent(TestUniformComponent)
157
it("should dispatch change event", () => {
168
const defaultValue = { bar: "hello" }
179
let changed: null | { bar: string } = null
1810
const wrapper = mount(
19-
<UniformTestComponent onChange={value => (changed = value)} value={defaultValue} />,
11+
<UniformComponent onChange={value => (changed = value)} defaultValue={defaultValue}>
12+
{data => <input onChange={ev => data.change.bar(ev.target.value)} type="string" />}
13+
</UniformComponent>,
2014
)
2115
const input = wrapper.find("input")
2216
input.simulate("change", { target: { value: "foo" } })
2317
expect(changed).toMatchObject({ bar: "foo" })
2418
})
25-
it("should allow ommiting onChange property", () => {
26-
const defaultValue = { bar: "hello" }
27-
const wrapper = mount(<UniformTestComponent value={defaultValue} onChange={() => null} />)
28-
const input = wrapper.find("input")
29-
input.simulate("change", { target: { value: "foo" } })
30-
})
3119
})

Diff for: src/UniformComponent.tsx

+21-31
Original file line numberDiff line numberDiff line change
@@ -1,37 +1,27 @@
1-
import React, { ComponentClass, SFC } from "react"
2-
import Data, { IData } from "handle-data-change"
1+
import React from "react"
2+
import Data from "handle-data-change"
33

4-
export interface IProps<D> {
5-
value: D
6-
onChange?: (newValue: D) => void
4+
export interface IProps<D = {}> {
5+
defaultValue: D
6+
value?: D
7+
onChange?: (value: D) => void
78
path?: string[]
9+
children: (data: Data<D>) => JSX.Element
810
}
911

10-
export interface UniformProps<D> {
11-
data: IData<D>
12-
}
13-
14-
function equal<T>(a: T, b: T) {
15-
return a === b
16-
}
17-
18-
type Omit<T, K> = Pick<T, Exclude<keyof T, K>>
19-
20-
export function UniformComponent<P extends UniformProps<any>, D = P["data"]["value"]>(
21-
Component: ComponentClass<P> | SFC<P>,
22-
) {
23-
let uniOnChange: (data: D) => void = () => null
24-
let uniValue: D = {} as D
25-
let uniPath: string[]
26-
let data = new Data<D>({} as D, uniOnChange)
27-
return function(props: IProps<D> & Omit<P, "data">) {
28-
const { onChange, value, defaultValue, path, ...rest } = props as any
29-
if (!equal(onChange, uniOnChange) || !equal(value, uniValue) || !equal(path, uniPath)) {
30-
uniOnChange = onChange
31-
uniValue = value
32-
uniPath = path
33-
data = new Data(value, onChange, path)
34-
}
35-
return <Component {...rest} data={data} />
12+
export class UniformComponent<D> extends React.Component<IProps<D>, D> {
13+
data = new Data(
14+
this.props.value || this.props.defaultValue,
15+
(data: D) => this.onChange(data),
16+
this.props.path,
17+
)
18+
state = this.data.value
19+
render() {
20+
this.data.value = this.props.value || this.data.value
21+
return this.props.children(this.data)
22+
}
23+
private onChange(value: D) {
24+
this.setState(value)
25+
this.props.onChange && this.props.onChange(value)
3626
}
3727
}

Diff for: src/UniformInput.tsx

+2-2
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,13 @@
11
import * as React from "react"
2-
import { IProps } from "./UniformComponent"
32
import { SafeJoin } from "./type-helpers"
43

54
export class UniformInput extends React.Component<
65
SafeJoin<
7-
SafeJoin<JSX.IntrinsicElements["input"], IProps<string>>,
6+
JSX.IntrinsicElements["input"],
87
{
98
value?: string
109
defaultValue?: string
10+
onChange?: (value: string) => void
1111
}
1212
>
1313
> {

Diff for: src/UniformInputNumber.tsx

+2-2
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,13 @@
11
import * as React from "react"
2-
import { IProps } from "./UniformComponent"
32
import { SafeJoin } from "./type-helpers"
43

54
export class UniformInputNumber extends React.Component<
65
SafeJoin<
7-
SafeJoin<JSX.IntrinsicElements["input"], IProps<number>>,
6+
JSX.IntrinsicElements["input"],
87
{
98
value?: number
109
defaultValue?: number
10+
onChange?: (value: number) => void
1111
}
1212
>
1313
> {

Diff for: src/index.test.tsx

+15-21
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
// tslint:disable:jsx-no-lambda
22
import * as React from "react"
33
import { mount, configure } from "enzyme"
4-
import { UniformProps, UniformInput, UniformInputNumber } from "./index"
4+
import { UniformInput, UniformInputNumber } from "./index"
55
import Adapter from "enzyme-adapter-react-16"
66
import { UniformComponent } from "./UniformComponent"
77

@@ -13,37 +13,31 @@ it("should work together", () => {
1313
firstName: string
1414
lastName: string
1515
}
16-
class SimpleUniform extends React.Component<UniformProps<ISimpleData>> {
16+
class SimpleUniform extends React.Component<{
17+
value: ISimpleData
18+
onChange: (data: ISimpleData) => void
19+
}> {
1720
render() {
1821
return (
19-
<form>
20-
<UniformInput
21-
onChange={this.props.data.change.firstName}
22-
value={this.props.data.value.firstName}
23-
/>
24-
<UniformInput
25-
onChange={this.props.data.change.lastName}
26-
value={this.props.data.value.lastName}
27-
/>
28-
<UniformInputNumber
29-
onChange={this.props.data.change.age}
30-
value={this.props.data.value.age}
31-
/>
32-
</form>
22+
<UniformComponent defaultValue={this.props.value} onChange={this.props.onChange}>
23+
{data => (
24+
<form>
25+
<UniformInput onChange={data.change.firstName} value={data.value.firstName} />
26+
<UniformInput onChange={data.change.lastName} value={data.value.lastName} />
27+
<UniformInputNumber onChange={data.change.age} value={data.value.age} />
28+
</form>
29+
)}
30+
</UniformComponent>
3331
)
3432
}
3533
}
3634

37-
const SimpleUniformWrapper = UniformComponent(SimpleUniform)
38-
3935
let update: ISimpleData = {
4036
age: 1,
4137
firstName: "foo",
4238
lastName: "bar",
4339
}
44-
const wrapper = mount(
45-
<SimpleUniformWrapper onChange={value => (update = value)} value={update} />,
46-
)
40+
const wrapper = mount(<SimpleUniform onChange={value => (update = value)} value={update} />)
4741
const inputs = wrapper.find("input")
4842
inputs.at(0).simulate("change", { target: { value: "my-firstname" } })
4943
expect(update).toMatchObject({

0 commit comments

Comments
 (0)