Skip to content

Commit

Permalink
feat(withFormikControl): Add standard use of withFormikControl
Browse files Browse the repository at this point in the history
  • Loading branch information
tpucci authored and Almouro committed Dec 15, 2018
1 parent e0d2442 commit ad5066a
Show file tree
Hide file tree
Showing 4 changed files with 254 additions and 0 deletions.
114 changes: 114 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,13 +7,15 @@ This repository is a set of high order components designed to help you take cont
**Features**

* Easily composable set of helpers
* Easily connect your [React Native Formik Standard Component](./doc/react-native-formik-standard-component.md) to the formik context
* Connects your React Native input to Formik with no boilerplate (See `makeReactNativeField`)
* Add a `type` prop on your TextInput to take care of the input options based on the type (See `withInputTypeProps`)
* Automatically focus the next input (See `withNextInputAutoFocus`)

**Table of contents**

* [Installation](#installation)
* [The Gist](#the-gist)
* [Advanced Example](#advanced-example)
* [Formatting inputs](#formatting-inputs)
* [API](#api)
Expand All @@ -33,6 +35,118 @@ This repository is a set of high order components designed to help you take cont
yarn add formik react-native-formik
```

## The Gist (with common use cases)

Once you designed `FocusableStandardRNFormikFieldComponent` and `StandardRNFormikFieldComponent` which respects the [React Native Formik Standard Component](./doc/react-native-formik-standard-component.md), here is an example to use those fields.

```js
// lib/Form.js
```
```js
import { compose } from 'recompose';
import { View } from 'react-native';
import {
withFormikControl,
withNextInputAutoFocusForm,
withNextInputAutoFocusInput,
} from 'react-native-formik';

import FocusableStandardRNFormikFieldComponent from 'app/src/components/FocusableStandardRNFormikField';

import StandardRNFormikFieldComponent from 'app/src/components/StandardRNFormikField';

const Container = withNextInputAutoFocusForm(View);
const FocusableStandardRNFormikField = compose(
withNextInputAutoFocusInput,
withFormikControl(FocusableStandardRNFormikFieldComponent),
);
const StandardRNFormikField = withFormikControl(StandardRNFormikFieldComponent);

export default {
Container,
FocusableStandardRNFormikField,
StandardRNFormikField,
};
```
```js
// pages/one_form.js
```
```js
import React, { Component } from 'react';
import { View, StyleSheet, TouchableHighlight, Text } from 'react-native';
import { Formik } from 'formik';
import * as Yup from 'yup';
import Form from 'app/src/lib/Form';

class OneForm extends Component {
validationSchema = Yup.object().shape({
firstNumber: Yup.number().required('required!'),
secondNumber: Yup.number().required('required!'),
comments: Yup.string().when('secondNumber', {
is: OneForm.shouldDisplayCommentsField,
then: Yup.string().required('required because second number is less than 5!'),
}), // Dynamic validation
email: Yup.string()
.required('required mate!')
.email("This is not an email")
.test(
'alreadyExists',
"This email already exists in our database :(",
value =>
value &&
new Promise(resolve => {
setTimeout(() => {
resolve(value && value.endsWith('exists'));
}, 2000);
}) // This asynchronouly validate the data
),
});

static shouldDisplayCommentsField = secondNumber => secondNumber && secondNumber < 5;

renderForm = ({ values, handleSubmit }) => (
<Form.Container style={styles.page}>
<Form.FocusableStandardRNFormikField name="email" />
<Form.StandardRNFormikFieldComponent name="firstNumber" />
<Form.StandardRNFormikFieldComponent name="secondNumber" />

{OneForm.shouldDisplayCommentsField(values.secondNumber) && (
<Form.StandardRNFormikFieldComponent name="comments" />
)} // Dynamic field rendering
<TouchableHighlight onPress={handleSubmit}>
<Text style={styles.submitButton}>OK</Text>
</TouchableHighlight>
</Form.Container>
);

render() {
return (
<Formik
onSubmit={values => console.log(values)}
validationSchema={this.validationSchema}
render={this.renderForm}
/>
);
}
}

const styles = StyleSheet.create({
page: {
backgroundColor: 'white',
flex: 1,
padding: 8,
},
submitButton: {
backgroundColor: 'blue',
textAlign: 'center',
padding: 4,
color: 'white',
},
});

export default OneForm;
```

## Advanced Example

Say we want to create a form with Material design inputs.
Expand Down
115 changes: 115 additions & 0 deletions doc/react-native-formik-standard-component.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,115 @@
# React Native Formik Standard Component

## Checks

- [ ] ⚛️ It is a react Stateless PureComponent
- [ ] 🎯 Its `PropsType` include the following typing
```js
type StandardReactNativeFormikFieldComponentProps = {
name: string,
error?: string,
value?: string | number,
onChange: (value: string | number) => any,
}
```
- [ ] ❔ (Optionnal) If you want it to work with React Native Formik `withNextInputAutoFocus` helper, it hhas a public `focus` method

## Good example

```js
// @flow
import React, { PureComponent } from 'react';
import { Text, TouchableHighlight, StyleSheet, Keyboard } from 'react-native';
import FieldContainer from './FieldContainer';

type Props = {
label: string,
error: string,
data: Array<{ label: string, value: string | number }>,
onChange: (value: string | number) => any,
};

class MultiChoices extends PureComponent<Props> {
focus = () => {
Keyboard.dismiss();
};

render() {
return (
<FieldContainer label={this.props.label} error={this.props.error}>
{this.props.data.map(choice => (
<Choice
key={choice.value}
onPress={this.props.onChange}
label={choice.label}
value={choice.value}
selected={this.props.value === choice.value}
/>
))}
</FieldContainer>
);
}
}

type ChoiceProps = {
label: string,
value: string | number,
onPress: (value: string | number) => any,
selected: boolean,
};

class Choice extends PureComponent<ChoiceProps> {
onPress = () => {
this.props.onPress(this.props.value);
};

render() {
return (
<TouchableHighlight onPress={this.onPress}>
<Text style={this.props.selected && styles.selectedChoice}>{this.props.label}</Text>
</TouchableHighlight>
);
}
}

type FieldContainerProps = {
error: string,
label: string,
children: Node,
};

class FieldContainer extends PureComponent<FieldContainerProps> {
render() {
return (
<View style={styles.container}>
<Text style={[styles.label, !!this.props.error && styles.error]}>{this.props.label}</Text>
{!!this.props.error && (
<Text style={[styles.errorLabel, styles.error]}>{this.props.error}</Text>
)}
{this.props.children}
</View>
);
}
}

const styles = StyleSheet.create({
container: {
marginBottom: 16,
},
label: {
fontWeight: '500',
marginBottom: 8,
},
selectedChoice: {
backgroundColor: 'green',
},
error: {
color: 'red',
},
errorLabel: {
marginBottom: 8,
},
});

export default MultiChoices;
```
10 changes: 10 additions & 0 deletions index.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,12 @@ export interface withTouchedProps {
name: string;
}

export interface withFormikControlProps {
error?: string;
value: string;
onChange: string;
}

export type makeInputGreatAgainProps = makeReactNativeFieldProps &
setFormikInitialValueProps &
withInputTypePropsProps &
Expand Down Expand Up @@ -66,4 +72,8 @@ export function makeInputGreatAgain<Props>(
WrappedComponent: React.ComponentType<Props>
): React.ComponentClass<Props & makeInputGreatAgainProps>;

export function withFormikControl<Props>(
WrappedComponent: React.ComponentType<Props>
): React.ComponentClass<Props & makeInputGreatAgainProps>;

export default makeInputGreatAgain;
15 changes: 15 additions & 0 deletions src/withFormikControl.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
import { withProps, compose } from "recompose";
import { withFormik } from "./withFormik";

const withFormikControl = compose(
withFormik,
withProps(ownProps => ({
error: ownProps.formik.submitCount
? ownProps.formik.errors[ownProps.name]
: undefined,
value: ownProps.formik.values[ownProps.name],
onChange: value => ownProps.formik.setFieldValue(ownProps.name, value)
}))
);

export default withFormikControl;

0 comments on commit ad5066a

Please sign in to comment.