Skip to content

Instantly share code, notes, and snippets.

@bbenezech
Last active August 23, 2017 04:56
Show Gist options
  • Star 1 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save bbenezech/528c7057c62300d41f51b212f0a92ad2 to your computer and use it in GitHub Desktop.
Save bbenezech/528c7057c62300d41f51b212f0a92ad2 to your computer and use it in GitHub Desktop.
redux-form 6 with typescript. Trigger warnings: no semi, multiline comma dangle
import * as React from 'react'
import { Field, WrappedFieldProps } from 'redux-form'
export interface OwnProps {
name: string,
type?: string,
label?: string,
help?: string,
}
interface P extends OwnProps, WrappedFieldProps {}
class BootstrapField extends React.PureComponent<P, {}> {
renderInput() {
// cherry-pick or unload the garbage trunk, as you like
const props = {
// you can add onFocus or what else
onChange: this.props.input.handleChange,
value: this.props.input.value,
id: this.props.name, // for labels, yo
type: this.props.type,
}
// you'll need a bit more to handle checkboxes and radios
// that's a story for another time
return props.type === 'textarea' ?
<textarea {...props} /> :
props.type === 'select' ?
<select {...props}>
{props.children}
</select> :
props.children ?
React.cloneElement(props.children as any, props) :
<input {...props} />
}
render() {
return <div className="form-group">
{this.props.label && <label htmlFor={this.props.name} className="control-label">
{this.props.label}
</label>}
<div className="input-group">{
this.renderInput()
}</div>
{this.props.help && <small className="text-muted">{this.props.help}</small>}
</div>
}
}
// create a RF Field typed with the props we want in our bootstrap Field
// and export with RF6's inversion of control® already included
// to get a nicer API in our forms
const MyCustomField = Field as new () => Field<OwnProps>
export default class extends React.PureComponent<OwnProps, {}> {
render = () => <MyCustomField component={BootstrapField} {...this.props} />
}
import * as React from 'react'
import BootstrapField from './BootstrapField'
import { reduxForm, getFormValues, FormProps, FieldArray } from 'redux-form'
import { connect } from 'react-redux'
import { AppState, Settings, DispatchProps } from './types'
interface StateProps {
formValues: Settings | undefined,
}
interface OwnProps extends FormProps<Settings, AppState> {
handleSave: (settings: Settings) => void,
}
interface P extends OwnProps, StateProps, DispatchProps {}
// notice that this.props.handleSubmit! and props.form! need a bang "!" when using strictNullChecks
// reason is complicated, you can have a look here: https://github.com/erikras/redux-form/pull/1318#issuecomment-231590672
class SettingsForm extends React.PureComponent<P, {}> {
render() {
// this.props.handleSubmit => redux form's handler
// this.props.handleSave => our controller's handler
return <form onSubmit={this.props.handleSubmit!(this.props.handleSave)}>
<legend>Settings</legend>
<BootstrapField name="sex" label="Sex" type="select">
<option key="" value="">""</option>
<option key="m" value="m">Male</option>
<option key="f" value="f">Female</option>
<option key="o" value="o">Other, complicated, will not disclose</option>
</BootstrapField>
<BootstrapField name="firstName" label="First name"/>
<BootstrapField name="lastName" label="Last name" help="Ask your family if not sure" />
<BootstrapField name="phone" label="Phone" type="phone" />
<div className="btn-toolbar">
<button type="submit" className="btn btn-primary" disabled={this.props.submitting || this.props.pristine}>
Process
</button>
</div>
</form>
}
}
// if you need additional stuff from app's state, you totally can connect your form
const mapStateToProps = (state: AppState, props: OwnProps): StateProps => ({
formValues: getFormValues<Settings>(props.form!)(state),
})
export default connect<OwnProps, StateProps, DispatchProps>(mapStateToProps)(reduxForm({})(SettingsForm))
import * as React from 'react'
import { connect } from 'react-redux'
import { AppState, Settings, DispatchProps } from './types'
import SettingsForm from './SettingsForm'
// whatever props you may need from owner
interface OwnProps {
}
// whatever props you may need from state
interface StateProps {
settings?: Settings
}
interface P extends OwnProps, StateProps, DispatchProps {}
class SettingsFormController extends React.PureComponent<P, {}> {
handleSave = (settings: Settings) => {
this.props.dispatch(/.../) // trigger things in your state
console.log('And now I know who you are', settings)
// do not google "And now I know who you are"
}
render() {
return <SettingsForm form="settings" handleSave={this.handleSave} />
}
}
const mapStateToProps = (state: AppState): StateProps => ({
settings: state.settings,
})
export default connect<OwnProps, StateProps, DispatchProps>(mapStateToProps)(SettingsFormController)
import { FormStateMap } from 'redux-form'
export type Settings = {
sex: 'm' | 'f' | 'o',
firstName: string,
lastName: string,
phone: string,
}
export type AppState {
form: FormStateMap,
settings?: Settings,
}
export type DispatchProps {
dispatch: Function
}
@bbenezech
Copy link
Author

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment