Skip to content

Commit b6494b3

Browse files
iusehooksAntonio
andauthored
Fix initial value undefined issue when passed as prop for useField - Input - Select - Colletction (#26)
Co-authored-by: Antonio <[email protected]>
1 parent 2f0a9f8 commit b6494b3

File tree

7 files changed

+281
-41
lines changed

7 files changed

+281
-41
lines changed

__tests__/Form.spec.js

Lines changed: 66 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,7 @@ import {
1212
} from "./helpers/components/ComplexForm";
1313
import { mountForm } from "./helpers/utils/mountForm";
1414

15-
import { Input } from "./../src";
15+
import { Input, Select, Collection, TextArea } from "./../src";
1616

1717
const dataTestid = "email";
1818
const typeInput = "text";
@@ -92,6 +92,71 @@ describe("Component => Form", () => {
9292
expect(onInit).toHaveReturnedWith(initialState);
9393
});
9494

95+
it("should override a initialized the Form state if Fields contain the value prop", () => {
96+
const initialState = {
97+
text: "foo",
98+
number: 1,
99+
checkbox: "1",
100+
radio: "2",
101+
range: 2,
102+
selectSingle: "1",
103+
selectMultiple: ["3"],
104+
object: { text: "foo" },
105+
object1: { text: "foo" },
106+
textarea: "foo",
107+
array: ["foo"],
108+
array1: ["foo"]
109+
};
110+
const props = { initialState, onInit };
111+
const children = [
112+
<Input type="text" name="text" value="BeBo" key="1" />,
113+
<Input type="number" name="number" value={10} key="2" />,
114+
<Input type="checkbox" name="checkbox" value="4" key="3" checked />,
115+
<Input type="radio" name="radio" value="6" key="4" checked />,
116+
<Input type="range" min="0" max="120" name="range" value={7} key="5" />,
117+
<Select name="selectSingle" value="2" key="6">
118+
<option value="" />
119+
<option value="1">1</option>
120+
<option value="2">2</option>
121+
</Select>,
122+
<Select key="7" multiple name="selectMultiple" value={["1", "2"]}>
123+
<option value="" />
124+
<option value="1">1</option>
125+
<option value="2">2</option>
126+
<option value="2">3</option>
127+
</Select>,
128+
<Collection key="8" object name="object">
129+
<Input type="text" name="text" value="BeBo" />,
130+
</Collection>,
131+
<Collection key="9" object name="object1" value={{ text: "BeBo" }}>
132+
<Input type="text" name="text" />,
133+
</Collection>,
134+
<TextArea name="textarea" value="BeBo" key="10" />,
135+
<Collection key="11" array name="array" value={["foo"]}>
136+
<Input type="text" value="BeBo" />,
137+
</Collection>,
138+
<Collection key="12" array name="array1" value={["BeBo"]}>
139+
<Input type="text" />,
140+
</Collection>
141+
];
142+
mountForm({ props, children });
143+
144+
expect(onInit).toHaveReturnedWith({
145+
text: "BeBo",
146+
number: 10,
147+
checkbox: "4",
148+
radio: "6",
149+
range: 7,
150+
selectSingle: "2",
151+
selectMultiple: ["1", "2"],
152+
object: { text: "BeBo" },
153+
object1: { text: "BeBo" },
154+
textarea: "BeBo",
155+
array: ["BeBo"],
156+
array1: ["BeBo"]
157+
});
158+
});
159+
95160
it("should reset the Form state", () => {
96161
const initialState = {
97162
user: { name: "foo", lastname: "anything", email: "[email protected]" }
Lines changed: 26 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,28 @@
1-
import React from "react";
1+
import React, { useEffect } from "react";
22
import { useField, withIndex } from "./../../../src";
33

4-
export const CustomField = withIndex(({ name, value, ...restAttr }) => {
5-
const props = useField({ type: "custom", name, value });
6-
const onChange = () => props.onChange({ target: { value: "5" } });
7-
return (
8-
<button type="button" onClick={onChange} {...restAttr}>
9-
Change Value
10-
</button>
11-
);
12-
});
4+
export const CustomField = withIndex(
5+
({
6+
type = "custom",
7+
name,
8+
value,
9+
jestFN = () => {},
10+
valueToChange = "5"
11+
}) => {
12+
const props = useField({ type, name, value });
13+
const onChange = () => props.onChange({ target: { value: valueToChange } });
14+
useEffect(() => {
15+
jestFN(props.value);
16+
}, []);
17+
return (
18+
<>
19+
<pre>
20+
<code data-testid="output">{JSON.stringify(props.value)}</code>
21+
</pre>
22+
<button type="button" data-testid="buttonChange" onClick={onChange}>
23+
Change Value
24+
</button>
25+
</>
26+
);
27+
}
28+
);

__tests__/helpers/components/InputCustom.jsx

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,3 +7,12 @@ export const InputCustom = withIndex(
77
return <input {...restAttr} {...props}></input>;
88
}
99
);
10+
11+
export const InputUseField = withIndex(({ type, name, value }) => {
12+
const props = useField({ type, name, value });
13+
return (
14+
<pre>
15+
<code>{JSON.stringify(props.value)}</code>
16+
</pre>
17+
);
18+
});

__tests__/hooks/useField.spec.js

Lines changed: 153 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -50,25 +50,174 @@ describe("Hooks => useField", () => {
5050
expect(onChange).toHaveBeenCalledWith({ number: "50", number2: "5" }, true);
5151
});
5252

53-
it("should render a Field of type custom with an initial value", () => {
53+
it("should render a Field of type custom with an initial value passed as prop", () => {
5454
const name = "custom";
5555
const props = { onInit, onChange };
5656
const initial = { a: "test" };
57+
const jestFN = jest.fn();
5758

5859
const children = [
59-
<CustomField key="1" data-testid={name} name={name} value={initial} />
60+
<CustomField key="1" name={name} value={initial} jestFN={jestFN} />
6061
];
6162
const { getByTestId } = mountForm({ children, props });
63+
64+
expect(jestFN).toHaveBeenCalledWith(initial);
65+
66+
expect(onInit).toHaveBeenCalledWith({ [name]: initial }, true);
67+
68+
const buttonChange = getByTestId("buttonChange");
69+
act(() => {
70+
fireEvent.click(buttonChange);
71+
});
72+
73+
expect(onChange).toHaveBeenCalledWith({ [name]: "5" }, true);
74+
});
75+
76+
it("should render a Field of type text with an initial value passed as prop", () => {
77+
const name = "text";
78+
const props = { onInit, onChange };
79+
const initial = "test";
80+
const jestFN = jest.fn();
81+
82+
const children = [
83+
<CustomField
84+
key="1"
85+
type="text"
86+
name={name}
87+
value={initial}
88+
jestFN={jestFN}
89+
/>
90+
];
91+
const { getByTestId } = mountForm({ children, props });
92+
93+
expect(jestFN).toHaveBeenCalledWith(initial);
94+
95+
expect(onInit).toHaveBeenCalledWith({ [name]: initial }, true);
96+
97+
const buttonChange = getByTestId("buttonChange");
98+
act(() => {
99+
fireEvent.click(buttonChange);
100+
});
101+
102+
expect(onChange).toHaveBeenCalledWith({ [name]: "5" }, true);
103+
});
104+
105+
it("should render a Field of type radio with an initial value passed as prop", () => {
106+
const name = "radio";
107+
const props = { onChange };
108+
const initial = "5";
109+
const jestFN = jest.fn();
110+
111+
const children = [
112+
<CustomField
113+
key="1"
114+
type="radio"
115+
name={name}
116+
value={initial}
117+
jestFN={jestFN}
118+
/>
119+
];
120+
const { getByTestId } = mountForm({ children, props });
121+
122+
expect(jestFN).toHaveBeenCalledWith(initial);
123+
124+
const buttonChange = getByTestId("buttonChange");
125+
act(() => {
126+
fireEvent.click(buttonChange);
127+
});
128+
129+
expect(onChange).toHaveBeenCalledWith({ [name]: "5" }, true);
130+
});
131+
132+
it("should render a Field of type checkbox with an initial value passed as prop", () => {
133+
const name = "checkbox";
134+
const props = { onChange };
135+
const initial = "5";
136+
const jestFN = jest.fn();
137+
138+
const children = [
139+
<CustomField
140+
key="1"
141+
type="checkbox"
142+
name={name}
143+
value={initial}
144+
jestFN={jestFN}
145+
/>
146+
];
147+
const { getByTestId } = mountForm({ children, props });
148+
149+
expect(jestFN).toHaveBeenCalledWith(initial);
150+
151+
const buttonChange = getByTestId("buttonChange");
152+
act(() => {
153+
fireEvent.click(buttonChange);
154+
});
155+
156+
expect(onChange).toHaveBeenCalledWith({ [name]: "5" }, true);
157+
});
158+
159+
it("should render a Field of type select single with an initial value passed as prop", () => {
160+
const name = "select";
161+
const props = { onInit, onChange };
162+
const initial = "5";
163+
const jestFN = jest.fn();
164+
165+
const children = [
166+
<CustomField
167+
key="1"
168+
type="select"
169+
data-testid="buttonChange"
170+
name={name}
171+
value={initial}
172+
jestFN={jestFN}
173+
/>
174+
];
175+
const { getByTestId } = mountForm({ children, props });
176+
177+
expect(jestFN).toHaveBeenCalledWith(initial);
178+
62179
expect(onInit).toHaveBeenCalledWith({ [name]: initial }, true);
63180

64-
const custom = getByTestId("custom");
181+
const buttonChange = getByTestId("buttonChange");
65182
act(() => {
66-
fireEvent.click(custom);
183+
fireEvent.click(buttonChange);
67184
});
68185

69186
expect(onChange).toHaveBeenCalledWith({ [name]: "5" }, true);
70187
});
71188

189+
it("should render a Field of type select multiple with an initial value passed as prop", () => {
190+
const name = "select";
191+
const props = { onInit, onChange };
192+
const initial = ["5", "6"];
193+
const valueAfterClick = ["7", "8"];
194+
195+
const jestFN = jest.fn();
196+
197+
const children = [
198+
<CustomField
199+
key="1"
200+
type="select"
201+
name={name}
202+
valueToChange={valueAfterClick}
203+
value={initial}
204+
jestFN={jestFN}
205+
/>
206+
];
207+
const { getByTestId } = mountForm({ children, props });
208+
209+
expect(jestFN).toHaveBeenCalledWith(initial);
210+
211+
expect(onInit).toHaveBeenCalledWith({ [name]: initial }, true);
212+
213+
const buttonChange = getByTestId("buttonChange");
214+
act(() => {
215+
fireEvent.click(buttonChange);
216+
});
217+
218+
expect(onChange).toHaveBeenCalledWith({ [name]: valueAfterClick }, true);
219+
});
220+
72221
it("should change a Field value due to an action", () => {
73222
const props = { onChange };
74223
const children = [

package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
{
22
"name": "usetheform",
3-
"version": "3.2.0",
3+
"version": "3.2.1",
44
"description": "React library for composing declarative forms in React and managing their state.",
55
"main": "./build/index.js",
66
"module": "./build/es/index.js",

src/hooks/useField.js

Lines changed: 25 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -72,29 +72,33 @@ export function useField(props) {
7272
context
7373
);
7474

75-
if (type === "checkbox" || type === "radio") {
75+
if (!isMounted.current && initialValueRef.current) {
7676
valueField.current = initialValueRef.current;
77-
checkedField.current =
78-
type === "checkbox"
79-
? state[nameProp.current] !== undefined ||
80-
(!isMounted.current && initialChecked === true)
81-
: state[nameProp.current] === initialValueRef.current;
82-
} else if (type === "select") {
83-
valueField.current =
84-
state[nameProp.current] !== undefined
85-
? state[nameProp.current]
86-
: !multiple
87-
? ""
88-
: [];
89-
} else if (type === "file") {
90-
valueField.current = state[nameProp.current];
91-
fileField.current =
92-
state[nameProp.current] !== undefined ? fileField.current : "";
93-
} else if (type === "custom") {
94-
valueField.current = state[nameProp.current];
9577
} else {
96-
valueField.current =
97-
state[nameProp.current] !== undefined ? state[nameProp.current] : "";
78+
if (type === "checkbox" || type === "radio") {
79+
valueField.current = initialValueRef.current;
80+
checkedField.current =
81+
type === "checkbox"
82+
? state[nameProp.current] !== undefined ||
83+
(!isMounted.current && initialChecked === true)
84+
: state[nameProp.current] === initialValueRef.current;
85+
} else if (type === "select") {
86+
valueField.current =
87+
state[nameProp.current] !== undefined
88+
? state[nameProp.current]
89+
: !multiple
90+
? ""
91+
: [];
92+
} else if (type === "file") {
93+
valueField.current = state[nameProp.current];
94+
fileField.current =
95+
state[nameProp.current] !== undefined ? fileField.current : "";
96+
} else if (type === "custom") {
97+
valueField.current = state[nameProp.current];
98+
} else {
99+
valueField.current =
100+
state[nameProp.current] !== undefined ? state[nameProp.current] : "";
101+
}
98102
}
99103

100104
const { current: applyReducers } = useRef(chainReducers(reducers));

src/hooks/useObject.js

Lines changed: 1 addition & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -70,10 +70,7 @@ export function useObject(props) {
7070

7171
// getValue from parent context
7272
if (!isMounted.current) {
73-
state.current =
74-
context.state[nameProp.current] !== undefined
75-
? context.state[nameProp.current]
76-
: init;
73+
state.current = initValue || context.state[nameProp.current] || init;
7774
} else {
7875
state.current =
7976
context.state[nameProp.current] || (isArray ? initArray : initObject);

0 commit comments

Comments
 (0)