Skip to content

Commit e0017c6

Browse files
authored
Allow later initialization through initialValues prop (#30)
1 parent 81e8103 commit e0017c6

File tree

4 files changed

+130
-0
lines changed

4 files changed

+130
-0
lines changed

src/ReactFinalForm.js

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@ import {
99
} from 'final-form'
1010
import type { Api, Config, FormSubscription, FormState } from 'final-form'
1111
import type { FormProps as Props, ReactContext } from './types'
12+
import shallowEqual from './shallowEqual'
1213
import renderComponent from './renderComponent'
1314
export const version = '1.0.0'
1415

@@ -94,6 +95,12 @@ export default class ReactFinalForm extends React.PureComponent<Props, State> {
9495
return this.form.submit()
9596
}
9697

98+
componentWillReceiveProps(nextProps: Props) {
99+
if (!shallowEqual(this.props.initialValues, nextProps.initialValues)) {
100+
this.form.initialize(nextProps.initialValues)
101+
}
102+
}
103+
97104
componentWillUnmount() {
98105
this.unsubscribe()
99106
}

src/ReactFinalForm.test.js

Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -181,6 +181,45 @@ describe('ReactFinalForm', () => {
181181
expect(onSubmit).toHaveBeenCalledWith({ foo: 'bar' })
182182
})
183183

184+
it('should reinitialize when initialValues prop changes', () => {
185+
const renderInput = jest.fn(({ input }) => <input {...input} />)
186+
class Container extends React.Component {
187+
state = { initValues: {} }
188+
189+
render() {
190+
return (
191+
<Form
192+
onSubmit={onSubmitMock}
193+
subscription={{ dirty: true }}
194+
initialValues={this.state.initValues}
195+
>
196+
{() => (
197+
<form>
198+
<Field name="foo" render={renderInput} />
199+
<button
200+
type="button"
201+
onClick={() => this.setState({ initValues: { foo: 'bar' } })}
202+
>
203+
Initialize
204+
</button>
205+
</form>
206+
)}
207+
</Form>
208+
)
209+
}
210+
}
211+
212+
const dom = TestUtils.renderIntoDocument(<Container />)
213+
expect(renderInput).toHaveBeenCalled()
214+
expect(renderInput).toHaveBeenCalledTimes(1)
215+
216+
const init = TestUtils.findRenderedDOMComponentWithTag(dom, 'button')
217+
TestUtils.Simulate.click(init)
218+
219+
expect(renderInput).toHaveBeenCalledTimes(2)
220+
expect(renderInput.mock.calls[1][0].input.value).toBe('bar')
221+
})
222+
184223
it('should return a promise from handleSubmit when submission is async', async () => {
185224
const onSubmit = jest.fn()
186225
let promise

src/shallowEqual.js

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
// @flow
2+
const shallowEqual = (a: any, b: any): boolean => {
3+
if (a === b) {
4+
return true
5+
}
6+
if (typeof a !== 'object' || !a || typeof b !== 'object' || !b) {
7+
return false
8+
}
9+
var keysA = Object.keys(a)
10+
var keysB = Object.keys(b)
11+
if (keysA.length !== keysB.length) {
12+
return false
13+
}
14+
var bHasOwnProperty = Object.prototype.hasOwnProperty.bind(b)
15+
for (var idx = 0; idx < keysA.length; idx++) {
16+
var key = keysA[idx]
17+
if (!bHasOwnProperty(key) || a[key] !== b[key]) {
18+
return false
19+
}
20+
}
21+
return true
22+
}
23+
24+
export default shallowEqual

src/shallowEqual.test.js

Lines changed: 60 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,60 @@
1+
import shallowEqual from './shallowEqual'
2+
3+
describe('shallowEqual', () => {
4+
it('returns false if either argument is null', () => {
5+
expect(shallowEqual(null, {})).toBe(false)
6+
expect(shallowEqual({}, null)).toBe(false)
7+
})
8+
9+
it('returns true if both arguments are null or undefined', () => {
10+
expect(shallowEqual(null, null)).toBe(true)
11+
expect(shallowEqual(undefined, undefined)).toBe(true)
12+
})
13+
14+
it('returns true if arguments are shallow equal', () => {
15+
expect(shallowEqual({ a: 1, b: 2, c: 3 }, { a: 1, b: 2, c: 3 })).toBe(true)
16+
})
17+
18+
it('returns false if arguments are not objects and not equal', () => {
19+
expect(shallowEqual(1, 2)).toBe(false)
20+
})
21+
22+
it('returns false if only one argument is not an object', () => {
23+
expect(shallowEqual(1, {})).toBe(false)
24+
})
25+
26+
it('returns false if first argument has too many keys', () => {
27+
expect(shallowEqual({ a: 1, b: 2, c: 3 }, { a: 1, b: 2 })).toBe(false)
28+
})
29+
30+
it('returns false if second argument has too many keys', () => {
31+
expect(shallowEqual({ a: 1, b: 2 }, { a: 1, b: 2, c: 3 })).toBe(false)
32+
})
33+
34+
it('returns true if values are not primitives but are ===', () => {
35+
let obj = {}
36+
expect(shallowEqual({ a: 1, b: 2, c: obj }, { a: 1, b: 2, c: obj })).toBe(
37+
true
38+
)
39+
})
40+
41+
it('returns false if arguments are not shallow equal', () => {
42+
expect(shallowEqual({ a: 1, b: 2, c: {} }, { a: 1, b: 2, c: {} })).toBe(
43+
false
44+
)
45+
})
46+
47+
it('should treat objects created by `Object.create(null)` like any other plain object', () => {
48+
function Foo() {
49+
this.a = 1
50+
}
51+
Foo.prototype.constructor = null
52+
53+
const object2 = { a: 1 }
54+
expect(shallowEqual(new Foo(), object2)).toBe(true)
55+
56+
const object1 = Object.create(null)
57+
object1.a = 1
58+
expect(shallowEqual(object1, object2)).toBe(true)
59+
})
60+
})

0 commit comments

Comments
 (0)