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

Use FormikApi + FormikState Subscriptions #3089

Closed
wants to merge 30 commits into from
Closed
Show file tree
Hide file tree
Changes from 12 commits
Commits
Show all changes
30 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
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
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
f3bd303
fixed SetValues type, adding functional variant
johnrom Apr 16, 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
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.pathToJest": "npm run test:vscode --",
}
38 changes: 38 additions & 0 deletions app/next.config.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
//
// Use Next Loaders on Linked Packages
// https://github.com/vercel/next.js/pull/13542#issuecomment-679085557
//
// eslint-disable-next-line @typescript-eslint/no-var-requires
const path = require('path');

module.exports = {
webpack: (config, { defaultLoaders, webpack }) => {
if (config.mode === 'development') {
config.module.rules = [
...config.module.rules,
{
test: /\.(tsx|ts|js|mjs|jsx)$/,
include: [path.resolve(config.context, '../')],
use: defaultLoaders.babel,
exclude: excludePath => {
return /node_modules/.test(excludePath);
},
},
];

// tsdx uses __DEV__
config.plugins.push(
new webpack.DefinePlugin({
__DEV__: process.env.NODE_ENV === 'development',
})
);
}

return config;
},

onDemandEntries: {
// Make sure entries are not getting disposed.
maxInactiveAge: 1000 * 60 * 60,
},
};
8 changes: 4 additions & 4 deletions app/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -8,10 +8,10 @@
"start": "next start"
},
"dependencies": {
"formik": "^2.1.5",
"formik": "^3.0.0-refs1",
"next": "9.5.3",
"react": "16.13.1",
"react-dom": "16.13.1",
"yup": "^0.29.3"
"react": "^17.0.1",
"react-dom": "^17.0.1",
"yup": "^0.28.1"
}
}
39 changes: 25 additions & 14 deletions app/pages/sign-in.js
Original file line number Diff line number Diff line change
@@ -1,8 +1,14 @@
import React, { useEffect, useState } from 'react';
import { ErrorMessage, Field, Form, FormikProvider, useFormik } from 'formik';
import { ErrorMessage, Field, Form, FormikProvider, FormikState, useFormik, useFormikApiComputedState } from 'formik';
import * as Yup from 'yup';
import { useRouter } from 'next/router';

const selectSignInState = (formikState) => ({
values: formikState.values,
errors: formikState.errors,
touched: formikState.touched,
});

const SignIn = () => {
const router = useRouter();
const [errorLog, setErrorLog] = useState([]);
Expand All @@ -22,35 +28,40 @@ const SignIn = () => {
},
});

const signInState = {
...formik.useState(selectSignInState),
...useFormikApiComputedState(formik),
};

useEffect(() => {
if (formik.errors.username && formik.touched.username) {
if (signInState.errors.username && signInState.touched.username) {
setErrorLog(logs => [
...logs,
{
name: 'username',
value: formik.values.username,
error: formik.errors.username,
value: signInState.values.username,
error: signInState.errors.username,
},
]);
}

if (formik.errors.password && formik.touched.password) {
if (signInState.errors.password && signInState.touched.password) {
setErrorLog(logs => [
...logs,
{
name: 'password',
value: formik.values.password,
error: formik.errors.password,
value: signInState.values.password,
error: signInState.errors.password,
},
]);
}
}, [
formik.values.username,
formik.errors.username,
formik.touched.username,
formik.values.password,
formik.errors.password,
formik.touched.password,
signInState.values.username,
signInState.errors.username,
signInState.touched.username,
signInState.values.password,
signInState.errors.password,
signInState.touched.password,
]);

return (
Expand All @@ -69,7 +80,7 @@ const SignIn = () => {
<ErrorMessage name="password" component="p" />
</div>

<button type="submit" disabled={!formik.isValid}>
<button type="submit" disabled={!signInState.isValid}>
Submit
</button>

Expand Down
8 changes: 7 additions & 1 deletion app/tsconfig.json
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,13 @@
"noEmit": true,
"moduleResolution": "node",
"resolveJsonModule": true,
"isolatedModules": true
"isolatedModules": true,
"baseUrl": "../",
"paths": {
"formik": [
"packages/formik/src"
],
}
},
"include": [
"next-env.d.ts",
Expand Down
3 changes: 2 additions & 1 deletion lerna.json
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,8 @@
"npmClient": "yarn",
"useWorkspaces": true,
"packages": [
"packages/*"
"packages/*",
"./app"
],
"command": {
"version": {
Expand Down
8 changes: 6 additions & 2 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -16,12 +16,16 @@
"typescript": "^4.0.3"
},
"workspaces": [
"packages/*"
"packages/*",
"./app"
],
"scripts": {
"lerna": "lerna",
"dev": "lerna run start --stream --parallel",
"test": "lerna run test --",
"test:watch": "lerna run test --stream --no-sort -- --watch",
"test:watchAll": "lerna run test --stream --no-sort -- --watchAll",
"test:vscode": "lerna run test --stream --no-sort --",
"build": "lerna run build",
"prepublish": "lerna run prepublish",
"format": "prettier --write 'examples/**/*' 'packages/*/src/**/*' 'website/src/**/*.{ts,tsx,js,jsx,md,mdx}' 'app/pages/**/*' 'packages/*/test/**/*' 'README.md'",
Expand All @@ -32,7 +36,7 @@
"cypress": "cypress run",
"precommit": "lint-staged",
"cypress:open": "cypress open",
"start:app": "npm run build && yarn --cwd packages/formik link && yarn --cwd node_modules/react link && yarn --cwd ./app link react formik && yarn --cwd ./app && yarn --cwd ./app run dev"
"start:app": "yarn --cwd ./app run dev"
},
"lint-staged": {
"**/*.{ts,tsx,md,mdx,js,jsx}": [
Expand Down
4 changes: 2 additions & 2 deletions packages/formik-native/package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "formik-native",
"version": "2.1.14",
"version": "3.0.0-refs1",
"license": "Apache-2.0",
"author": "Jared Palmer <jared@palmer.net>",
"repository": "formium/formik",
Expand Down Expand Up @@ -31,7 +31,7 @@
"react": ">=16.8.0"
},
"dependencies": {
"formik": "2.2.6"
"formik": "3.0.0-refs1"
},
"devDependencies": {
"@react-native-community/eslint-config": "^0.0.5",
Expand Down
4 changes: 2 additions & 2 deletions packages/formik-native/src/index.ts
Original file line number Diff line number Diff line change
@@ -1,9 +1,9 @@
import * as React from 'react';
import { NativeSyntheticEvent, NativeTouchEvent } from 'react-native';
import { useFormikContext } from 'formik';
import { useFormikApi } from 'formik';

export function useSubmitButton() {
const { submitForm } = useFormikContext();
const { submitForm } = useFormikApi();
const handlePress = React.useCallback(
(_ev: NativeSyntheticEvent<NativeTouchEvent>) => {
submitForm();
Expand Down
10 changes: 7 additions & 3 deletions packages/formik/package.json
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
{
"name": "formik",
"description": "Build forms in React, without the tears",
"version": "2.2.6",
"version": "3.0.0-refs1",
"license": "Apache-2.0",
"author": "Jared Palmer <jared@palmer.net> (https://jaredpalmer.com)",
"repository": "formium/formik",
Expand Down Expand Up @@ -30,7 +30,9 @@
"umd:main": "dist/formik.umd.production.js",
"module": "dist/formik.esm.js",
"typings": "dist/index.d.ts",
"files": ["dist"],
"files": [
"dist"
],
"peerDependencies": {
"react": ">=16.8.0"
},
Expand All @@ -49,14 +51,16 @@
"lodash-es": "^4.17.14",
"react-fast-compare": "^2.0.1",
"tiny-warning": "^1.0.2",
"tslib": "^1.10.0"
"tslib": "^1.10.0",
"use-subscription": "^1.5.1"
},
"devDependencies": {
"@testing-library/react": "^11.1.0",
"@types/hoist-non-react-statics": "^3.3.1",
"@types/lodash": "^4.14.119",
"@types/react": "^16.9.55",
"@types/react-dom": "^16.9.9",
"@types/use-subscription": "^1.0.0",
"@types/warning": "^3.0.0",
"@types/yup": "^0.24.9",
"just-debounce-it": "^1.1.0",
Expand Down
71 changes: 24 additions & 47 deletions packages/formik/src/ErrorMessage.tsx
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
import * as React from 'react';
import { FormikContextType } from './types';
import { getIn, isFunction } from './utils';
import { connect } from './connect';
import { isFunction } from './utils';
import { useFieldMeta } from './hooks/hooks';

export interface ErrorMessageProps {
name: string;
Expand All @@ -11,48 +10,26 @@ export interface ErrorMessageProps {
render?: (errorMessage: string) => React.ReactNode;
}

class ErrorMessageImpl extends React.Component<
ErrorMessageProps & { formik: FormikContextType<any> }
> {
shouldComponentUpdate(
props: ErrorMessageProps & { formik: FormikContextType<any> }
) {
if (
getIn(this.props.formik.errors, this.props.name) !==
getIn(props.formik.errors, this.props.name) ||
getIn(this.props.formik.touched, this.props.name) !==
getIn(props.formik.touched, this.props.name) ||
Object.keys(this.props).length !== Object.keys(props).length
) {
return true;
} else {
return false;
}
}
export function ErrorMessage({
component,
render,
children,
name,
...rest
}: ErrorMessageProps): JSX.Element | null {
const { touched, error } = useFieldMeta(name);

render() {
let { component, formik, render, children, name, ...rest } = this.props;

const touch = getIn(formik.touched, name);
const error = getIn(formik.errors, name);

return !!touch && !!error
? render
? isFunction(render)
? render(error)
: null
: children
? isFunction(children)
? children(error)
: null
: component
? React.createElement(component, rest as any, error)
: error
: null;
}
}

export const ErrorMessage = connect<
ErrorMessageProps,
ErrorMessageProps & { formik: FormikContextType<any> }
>(ErrorMessageImpl);
return !!touched && !!error
? render
? isFunction(render)
? render(error)
: null
: children
? isFunction(children)
? children(error)
: null
: component
? React.createElement(component, rest as any, error)
: (error as any)
: null;
};
Loading