Skip to content

Instantly share code, notes, and snippets.

@Aryk
Created May 1, 2017 22:20
Show Gist options
  • Save Aryk/5f69961736f4a546104587196d3ceab5 to your computer and use it in GitHub Desktop.
Save Aryk/5f69961736f4a546104587196d3ceab5 to your computer and use it in GitHub Desktop.
formHelper for react-redux-form
import { Reducer } from 'redux';
import { combineForms, FormState, FieldState } from 'react-redux-form';
export interface IFormHelper<Model> {
modelPath: string;
attributePath: (fieldName: keyof Model) => string;
modelAttributePath: (fieldName: keyof Model) => string;
dataSelector: (store: any) => Model;
fieldMetaDataSelector: (store: any) => (field: keyof Model) => FieldState;
formMetaDataSelector: (store: any) => FormState;
formEntry: (initialStateOrReducer: Partial<Model> | Reducer<any>) => object;
}
/**
* We namespace everything form related under the 'form' reducer key, including the form states. The forms should not
* be where the model state is stored since forms are transient and can be cleared off and temporarily changed.
*/
const reducerKeyName = 'form';
const formMetaDataKey = 'fieldMeta';
/**
* Creates nested forms for react-redux-form. The idea here is to have a nested heirarchy as such:
*
* form.video.title //=> 'My Favorite Video'
* form.fieldMeta.video.title //=> <field>
* form.fieldMeta.video.title.focus //=> true
* form.fieldMeta.video.title.touched //=> false
*
* Please read: https://davidkpiano.github.io/react-redux-form/docs/api/formReducer.html for more information about
* the <field> structure.
*
* The default heirarchy would name "fieldMeta" as "forms", which is kind of confusing since the "forms" namespace
* does not actually include the field value, only metadata about the form.
*
*/
const formsReducerEntry = (forms: object) => ({
[reducerKeyName]: combineForms(forms, reducerKeyName, {key: formMetaDataKey}),
});
const formHelper = <Model>(formName: string): IFormHelper<Model> => {
const modelPath = [reducerKeyName, formName].join('.'); // ie form.user
// The signifance of this is that it allows us to check again the FieldNameType for matches.
const attributePath = fieldName => '.'.concat(fieldName); // ie .firstName
return {
modelPath,
modelAttributePath: fieldName => modelPath.concat(attributePath(fieldName)), // ie form.user.firstName
attributePath,
dataSelector: mainState => mainState[reducerKeyName][formName],
fieldMetaDataSelector: mainState => field => mainState[reducerKeyName][formMetaDataKey][formName][field],
formMetaDataSelector: mainState => mainState[reducerKeyName][formMetaDataKey][formName].$form,
/**
* Creates the entry to be put into the reducer where the key is the store key and value is the reducer.
*/
formEntry: initialStateOrReducer => ({[formName]: initialStateOrReducer}),
};
};
export { formHelper, formsReducerEntry };
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment