Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Strongly Typed Formik, v3 #3152

Draft
wants to merge 113 commits into
base: milestone/v3
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
113 commits
Select commit Hold shift + click to select a range
9f8765e
Started to reimplement subscriber.
johnrom Mar 8, 2021
4dc160b
Grab types from other PR, to simplify later.
johnrom Mar 9, 2021
ef66e38
Grab types from other PR, to simplify later.
johnrom Mar 9, 2021
b77d371
Merge branch 'johnrom/subscriber' of github.com:johnrom/formik into j…
johnrom Mar 9, 2021
3e6e20c
Set up dev environment and implement Field.
johnrom Mar 10, 2021
881d7e6
Memoize Formik API and make it rain optimized states.
johnrom Mar 11, 2021
b8e7fcd
Roll back unnecessary tsconfig change.
johnrom Mar 11, 2021
feaee0e
Fix Sign In App Page.
johnrom Mar 11, 2021
a5a2f47
Roll back unnecessary tsconfig change.
johnrom Mar 11, 2021
4037473
Wire up FormikConsumer.
johnrom Mar 11, 2021
e2c496c
Remove implementation details from deprecated comments.
johnrom Mar 11, 2021
a78459b
Don't expose internal useOptimizedSelector.
johnrom Mar 11, 2021
747ce07
Reduce maintenance surface by moving useOptimizedSelector into its ow…
johnrom Mar 15, 2021
9d10f82
Update use-optimized-selector
johnrom Mar 17, 2021
0d1cbfd
Move useFormikComputedState to api.useComputedState, useIsDirty and u…
johnrom Mar 17, 2021
0c18ebb
Accidental import change.
johnrom Mar 17, 2021
3ab46cd
Remove TSConfig path aliases during build.
johnrom Mar 17, 2021
5011168
If we're calling our own reducer, we don't need to useReducer! setSta…
johnrom Mar 18, 2021
a5f4643
Optimize field meta and computed state with shallow equals.
johnrom Mar 18, 2021
c9b9579
Move Computed State into normal state helpers so that FormikState = F…
jawnrom Mar 22, 2021
611432a
Merge pull request #3 from jawnstreams/johnrom/downstream
johnrom Mar 22, 2021
dc2e2fa
Clarify RenderState vs GetState. Fix Test.
johnrom Mar 22, 2021
0764e12
Fix tests with updated /app structure.
johnrom Mar 22, 2021
0079948
Remove unused helper components from app/
johnrom Mar 23, 2021
9c4cd01
Ignore TypeScript Build Errors during app/ build.
johnrom Mar 23, 2021
ba1fdb9
Add Changeset
johnrom Mar 23, 2021
2d613c1
Remove create table
jaredpalmer Mar 24, 2021
e800bf8
Add Parse / Format to Subscriptions PR
johnrom Mar 24, 2021
c06dff8
Add changes from FieldArray over reducer-refs PR.
johnrom Mar 24, 2021
ba6f6c0
Add Field Array to App Index
johnrom Mar 24, 2021
70de3d1
Fix UseField type definition, add micro-hooks.
johnrom Mar 25, 2021
a8e5cbf
Separate FormikContext and FormikConfigContext
johnrom Mar 25, 2021
d718684
Add FormikConfig to connect, some type fixes.
johnrom Mar 25, 2021
df779d4
Merge branch 'johnrom/subscriber' into johnrom/use-optimized-selector
johnrom Mar 29, 2021
6d2fce7
update relative links to include extension (#3134)
gingerrific Apr 5, 2021
dfde363
Isolate subscriptions logic from useFormik to enforce correct use of …
johnrom Apr 12, 2021
46a6e17
Fix useFormikState documentation, fix getState stability.
johnrom Apr 12, 2021
ee43bb2
Get rid of unnecessary, autocompleted import.
johnrom Apr 12, 2021
7949c3d
React.SFC -> FC
johnrom Apr 12, 2021
94b8f22
Null coalesced errors and touched during resetForm.
johnrom Apr 12, 2021
2e75c75
Merge branch 'johnrom/subscriber' into johnrom/parse-format-subscript…
johnrom Apr 13, 2021
6355689
Merge branch 'johnrom/subscriber' into johnrom/field-array-subscriptions
johnrom Apr 13, 2021
2327d3b
Merge branch 'johnrom/parse-format-subscriptions' into johnrom/micro-…
johnrom Apr 13, 2021
39f7cfd
Merge branch 'johnrom/subscriber' into johnrom/use-optimized-selector
johnrom Apr 13, 2021
06b4bbb
Merge branch 'johnrom/subscriber' into johnrom/use-formik-config
johnrom Apr 13, 2021
00dac43
Tiny Field Fixes
johnrom Apr 13, 2021
f49f9e2
Update App/ to reflect config changes.
johnrom Apr 13, 2021
86eafd6
Fixed other instance of useFormik.
johnrom Apr 13, 2021
1821200
Move render optimization to FormikProvider.
johnrom Apr 14, 2021
ea22bab
Merge branch 'johnrom/field-array-subscriptions' into johnrom/v3
johnrom Apr 14, 2021
1060bfd
Merge branch 'johnrom/use-optimized-selector' into johnrom/v3
johnrom Apr 14, 2021
48c5771
Merge branch 'johnrom/micro-hooks-and-field-fixes' into johnrom/v3
johnrom Apr 14, 2021
c4e7a41
Merge branch 'johnrom/use-formik-config' into johnrom/v3
johnrom Apr 14, 2021
6d08add
Test packages.
johnrom Apr 14, 2021
f3bd303
fixed SetValues type, adding functional variant
johnrom Apr 16, 2021
a9216a0
Merge branch 'johnrom/subscriber' into johnrom/v3
johnrom Apr 16, 2021
7d31376
Strongly typed Formik & FormikRegistration. Next up: Fields.
johnrom Apr 17, 2021
faa1603
Need to figure out how to map to a flat list of possible Field Names …
jawnrom Apr 18, 2021
9184eef
Strongly typed Fields part 2... almost there
johnrom Apr 21, 2021
c5eff7c
Fix App/
johnrom Apr 21, 2021
64b764a
Move test code out, write a strongly typed tutorial in App/. Need to …
johnrom Apr 21, 2021
d137f31
Even strengthier. Need to figure out why auto-inference of TypedField…
johnrom Apr 21, 2021
cc17208
We can now do both `Value = FieldValue<Values, Path>` and `AnyName = …
johnrom Apr 22, 2021
37e9a1b
Fixes, also adding a test for array fields.
johnrom Apr 22, 2021
096cb92
Add number type to NameOf to support array paths.
johnrom Apr 23, 2021
9603f45
Testing and testing and testing... Can't get the inference 100% right.
johnrom Apr 23, 2021
56b7146
Testing Omit<ExtraProps> again.
johnrom Apr 23, 2021
0beed10
Doing some serious type checking. Need to be able to match paths with…
johnrom Apr 23, 2021
19f02d3
Enforced PathMatchingValue<>. Tested out a strongly typed Field in app/.
johnrom Apr 25, 2021
585371b
Getting there, but can't get the right inference between ComponentTyp…
johnrom Apr 25, 2021
3121589
Component works the same as As now.
johnrom Apr 25, 2021
8965788
Starting on FieldArray.
johnrom Apr 25, 2021
9173d00
ExtraProps Extends wasn't doing anything.
johnrom Apr 26, 2021
0397649
Removed ExtraProps to test without it. The benefit is questionable, w…
johnrom Apr 26, 2021
9d84a68
Custom Fields + AsFields + ComponentFields, fully inferred with cheap…
johnrom Apr 28, 2021
a4d9e29
Using only Value + PathMatchingValue. Lose a lil bit of intellisense …
johnrom Apr 29, 2021
dfb1a18
Strengthened PassThroughConfig so we can remove all our casts.
johnrom Apr 29, 2021
76c3948
Allow `any` prop components to work.
johnrom Apr 29, 2021
7bca09e
Switched from `<Values, Value>` to `<Value, Values>` for consistency.…
johnrom Apr 29, 2021
1439e67
Fix to support both Custom Fields and AnyProps.
johnrom Apr 30, 2021
bc96842
Fix FastField
johnrom Apr 30, 2021
b857119
Fix package.json
johnrom Apr 30, 2021
c98b694
Whoops, 3.1.0
johnrom Apr 30, 2021
03b8c48
Type arrays with a magical "0" placeholder, and then replace 0 with $…
johnrom Apr 30, 2021
f7d3ac8
Tiny tweaks.
johnrom Apr 30, 2021
53ba9f0
Add default export to FieldTestsPage
johnrom May 4, 2021
a1016f3
Change back to old readmes and stuff, only use experimental readmes o…
johnrom May 4, 2021
572dcf7
Add another changeset for types
johnrom May 4, 2021
14e38b7
Add fallback types for Typescript < 4.1
johnrom May 4, 2021
96c7a91
Whoops, backwards!
johnrom May 4, 2021
9b7300f
add an untyped ts build to redirect
johnrom May 5, 2021
7d59592
Add untyped build and ignore folder.
johnrom May 5, 2021
26e4e8d
Docs patches (#3192)
jaredpalmer May 12, 2021
43e95a6
update typesVersions and files in package.json
johnrom May 14, 2021
f9aed8e
Added PathLikeValue to widen possibilities for setFieldValue. Now set…
johnrom May 14, 2021
2e1338c
Accept either PathMatchingValue or PathLikeValue for setFieldValue.
johnrom May 14, 2021
dba1742
Switched to interfaces where possible without breaking TS.
johnrom May 16, 2021
7fa2c8f
Docs: @types/yup no longer required (#3200)
espipj May 18, 2021
61c3e92
fix: add users company URL (#3197)
parasg1999 May 18, 2021
31405ab
Fix batching error updates (during mount) (#3201)
artola May 19, 2021
261ee30
Version Packages (#2970)
github-actions[bot] May 19, 2021
3a9c707
fix: value is same as message in setFieldError (#3203)
hixus May 21, 2021
2bfa592
Version Packages (#3206)
github-actions[bot] May 21, 2021
ca60ef9
Bump lodash and lodash-es to latest versions. (#3227)
johnrom Jun 2, 2021
903c110
Version Packages (#3228)
github-actions[bot] Jun 2, 2021
4821467
Set package.json versions back to their originals, so changeset can u…
johnrom Jun 4, 2021
e0b804f
Merge remote-tracking branch 'upstream/master' into johnrom/v3
johnrom Jun 4, 2021
788c258
Merge remote-tracking branch 'upstream/master' into johnrom/v3-remerged
johnrom Jun 18, 2021
75c3c4d
Merge branch 'johnrom/v3-remerged' into johnrom/v3
johnrom Jun 18, 2021
f24e151
Re-merge these files because apparently they failed last time.
johnrom Jun 18, 2021
51502dc
Do basic radio / checkbox parsing, and put all the value parse logic …
johnrom Jul 8, 2021
58b538f
Merge pull request #7 from johnrom/johnrom/parsing-refactor
johnrom Jul 8, 2021
b4d6962
Merge remote-tracking branch 'origin/johnrom/v3' into johnrom/strong-…
johnrom Jul 30, 2021
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 0 additions & 5 deletions .changeset/brown-jokes-act.md

This file was deleted.

10 changes: 10 additions & 0 deletions .changeset/fifty-shrimps-leave.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
---
"formik-native": major
"formik": major
"app": major
---

Use FormikApi + FormikState Subscriptions

Switch underlying state implementation to use a subscription, and access that subscription via `useFormik().useState()` or its alias, `useFormikState()`.
useFormikContext() only contains a stable API as well as some config props which should eventually be moved to a separate FormikConfigContext for further optimization.
6 changes: 6 additions & 0 deletions .changeset/flat-peaches-hug.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
---
'formik': major
'app': minor
---

Add strongly typed fields in an opt-in fashion using useTypedField() and useTypedFieldArray().
5 changes: 0 additions & 5 deletions .changeset/thirty-socks-whisper.md

This file was deleted.

5 changes: 5 additions & 0 deletions .env.sample
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
# Created by Vercel CLI
NEXT_PUBLIC_GA_TRACKING_ID="xxxx"
GA_TRACKING_ID="XXXXX"
SENTRY_DSN="XXXX"
BLOG_INDEX_ID="XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX"
5 changes: 3 additions & 2 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
dist
dist-untyped
compiled
*.log
coverage
Expand All @@ -16,9 +17,9 @@ yarn.lock

website/translated_docs
website/build
website/yarn.lock
!website/yarn.lock
website/node_modules
website/i18n
!website2/yarn.lock

.env
.vercel
49 changes: 49 additions & 0 deletions .vscode/launch.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
{
// Use IntelliSense to learn about possible attributes.
// Hover to view descriptions of existing attributes.
// For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387
"version": "0.2.0",
"configurations": [
{
"name": "Run NPM Dev",
"request": "launch",
"type": "node",
"runtimeArgs": [
"run",
"start:app"
],
"runtimeExecutable": "npm",
"cwd": "${workspaceFolder}",
"skipFiles": [
"<node_internals>/**"
]
},
{
"type": "node",
"name": "vscode-jest-tests",
"request": "launch",
"runtimeExecutable": "${workspaceRoot}/node_modules/.bin/lerna",
"args": [
"run",
"test",
"--",
"--runInBand",
// when filtering, every package not matching will have no tests
"--passWithNoTests",
"--testTimeout=9999"
],
"cwd": "${workspaceFolder}",
"console": "integratedTerminal",
"protocol": "inspector",
"internalConsoleOptions": "neverOpen",
"disableOptimisticBPs": true
},
{
"type": "chrome",
"request": "launch",
"name": "Launch Chrome against localhost",
"url": "http://localhost:3000",
"webRoot": "${workspaceFolder}/app"
}
]
}
3 changes: 2 additions & 1 deletion .vscode/settings.json
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
{
"typescript.tsdk": "node_modules/typescript/lib"
"typescript.tsdk": "node_modules/typescript/lib",
"jest.jestCommandLine": "npm run test:vscode --",
}
21 changes: 21 additions & 0 deletions app/components/debugging/Collapse.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
import * as React from 'react';

export const Collapse: React.FC = props => {
const [collapsed, setCollapsed] = React.useState(false);

return (
<div>
<button type="button" onClick={() => setCollapsed(!collapsed)}>
Collapse
</button>
<div
style={{
overflow: 'hidden',
height: collapsed ? 0 : 'auto',
}}
>
{props.children}
</div>
</div>
);
};
19 changes: 19 additions & 0 deletions app/components/debugging/DebugProps.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
import * as React from 'react';

export const DebugProps = (props?: any) => {
const renderCount = React.useRef(0);
return (
<div style={{ margin: '1rem 0' }}>
<pre
style={{
background: '#f6f8fa',
fontSize: '.65rem',
padding: '.5rem',
}}
>
<strong>props</strong> = {JSON.stringify(props, null, 2)}
<strong>renders</strong> = {renderCount.current++}
</pre>
</div>
);
};
24 changes: 24 additions & 0 deletions app/components/fields/email-field-as-class.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
import { FieldAsClass, FieldAsProps } from 'formik';
import React from 'react';

export class EmailFieldAsClass extends FieldAsClass<string> {
render() {
return (
<div className="email-field">
<label>
Email
<input
type={this.props.type}
name={this.props.name}
value={this.props.value}
onChange={this.props.onChange}
onBlur={this.props.onBlur}
/>
</label>
</div>
);
}
}

export const TypedEmailField: React.ComponentClass<FieldAsProps<string>> =
EmailFieldAsClass;
16 changes: 16 additions & 0 deletions app/components/fields/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
import { NumberField } from 'app/components/fields/number-field';
import {
createCustomField,
createTypedField,
createTypedFieldArray
} from 'formik';
import { useMemo } from 'react';

export const createTypedFields = <Values,>() => ({
Field: createTypedField<Values>(),
FieldArray: createTypedFieldArray<Values>(),
NumberField: createCustomField<Values>()(NumberField),
});

export const useTypedFields = <Values,>() =>
useMemo(() => createTypedFields<Values>(), []);
31 changes: 31 additions & 0 deletions app/components/fields/number-as-field.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
import { parseNumberOrEmpty, formatNumberOrEmpty } from "app/helpers/parse-format-helpers";
import {
FieldAsProps,
FormatFn,
ParseFn,
useFormikContext
} from "formik";
import React from "react";

export const NumberAsField = <
Values,
>(props: FieldAsProps<number | "", Values>) => {
const formik = useFormikContext<Values>();
const parse: ParseFn<number | ""> = props.parse ?? parseNumberOrEmpty;
const format: FormatFn<number | ""> = props.format ?? formatNumberOrEmpty;

const handleChange = (event: React.ChangeEvent<HTMLInputElement>) => {
const parsedValue = parse(event.target.value, props.name);
console.log(parsedValue);
formik.setFieldValue(props.name, parsedValue);
}

return <input
type="text"
name={props.name}
value={format(props.value, props.name)}
onChange={handleChange}
onBlur={formik.handleBlur}
inputMode="numeric"
/>
};
20 changes: 20 additions & 0 deletions app/components/fields/number-component-field.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
import { parseNumberOrEmpty } from "app/helpers/parse-format-helpers";
import {
ParseFn,
TypedComponentField,
} from "formik";

export const NumberComponentField: TypedComponentField<number | ""> = (props) => {
const parse: ParseFn<number | ""> = props.parse ?? parseNumberOrEmpty;

const handleChange = (value: unknown) => {
props.form.setFieldValue(props.name, parse(value, props.name));
}

return <input
{...props.field}
type="text"
onChange={handleChange}
inputMode="numeric"
/>
};
25 changes: 25 additions & 0 deletions app/components/fields/number-field.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
import { formatNumberOrEmpty, parseNumberOrEmpty } from "app/helpers/parse-format-helpers";
import {
CustomField,
FieldConfig,
} from "formik";
import React from "react";
import { useTypedFields } from ".";
import { NumberAsField } from "./number-as-field";

export const NumberField: CustomField<number | ""> = <Values,>(
props: FieldConfig<
number | "",
Values
>
) => {
const fields = useTypedFields<Values>();

return <fields.Field
type="text"
name={props.name}
format={(formatNumberOrEmpty)}
parse={parseNumberOrEmpty}
as={NumberAsField}
/>
};
46 changes: 46 additions & 0 deletions app/components/fields/number-repeater-field.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
import {
Field,
FieldArrayProps,
} from "formik";
import React from "react";
import { NumberAsField } from "./number-as-field";

export const NumberRepeaterField = <
Values,
>(props: FieldArrayProps<
number | "",
Values
>) => {
return <div>
{props.field.value?.length > 0
? props.field.value.map((_value, index) => (
<div key={index}>
<Field
name={`${props.name}.${index}`}
as={NumberAsField}
type="number"
/>
<button
type="button"
// remove a friend from the list
onClick={() => props.remove(index)}
>
-
</button>
<button
type="button"
// insert an empty string at a position
onClick={() => props.insert(index, '')}
>
+
</button>
</div>
))
:
// show this when user has removed all friends from the list
<button type="button" onClick={() => props.push('')}>
Add a friend
</button>
}
</div>
};
30 changes: 30 additions & 0 deletions app/components/fields/phone-as-field.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
import { parsePhoneOrEmpty } from "app/helpers/parse-format-helpers";
import {
FieldAsProps,
ParseFn,
TypedAsField,
useFormikContext
} from "formik";
import React from "react";

export const PhoneAsField: TypedAsField<string> = <
Values,
>(props: FieldAsProps<string, Values>) => {
const formik = useFormikContext<Values>();
const parse: ParseFn<string> = props.parse ?? parsePhoneOrEmpty;

const handleChange = (event: React.ChangeEvent<HTMLInputElement>) => {
const parsedValue = parse(event.target.value, props.name);
console.log(parsedValue);
formik.setFieldValue(props.name, parsedValue);
}

return <input
type="text"
name={props.name}
value={props.format ? props.format(props.value, props.name) : props.value}
onChange={handleChange}
onBlur={formik.handleBlur}
inputMode="numeric"
/>
};
9 changes: 9 additions & 0 deletions app/helpers/array-helpers.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
import { selectRandomInt } from './random-helpers';

export const selectRange = (count: number) => Array.from(Array(count).keys());

export const selectRandomArrayItem = <T extends any>(array: T[]) => {
const index = selectRandomInt(array.length);

return array[index];
};
Loading