Skip to content

Instantly share code, notes, and snippets.

@blvdmitry
Created September 15, 2018 21:06
Show Gist options
  • Save blvdmitry/89a7fecf614e3de80dead0e9e395262a to your computer and use it in GitHub Desktop.
Save blvdmitry/89a7fecf614e3de80dead0e9e395262a to your computer and use it in GitHub Desktop.
import actionTypes from './forms.actionTypes';
export const addForm = payload => ({ type: actionTypes.ADD_FORM, payload });
export const removeForm = payload => ({ type: actionTypes.REMOVE_FORM, payload });
export const addField = payload => ({ type: actionTypes.ADD_FIELD, payload });
export const removeField = payload => ({ type: actionTypes.REMOVE_FIELD, payload });
export const setValue = payload => ({ type: actionTypes.SET_VALUE, payload });
export const setError = payload => ({ type: actionTypes.SET_ERROR, payload });
import React from 'react';
import { connect } from 'react-redux';
import { addForm, removeForm } from 'ducks/forms/forms.actions';
class Form extends React.PureComponent {
componentWillMount() {
const { dispatch, id } = this.props;
dispatch(addForm({ formId: id }));
}
componentWillUnmount() {
const { dispatch, id } = this.props;
dispatch(removeForm({ formId: id }));
}
handleSubmit = (event) => {
event.preventDefault();
const { onSubmit, fields } = this.props;
const values = {};
const errors = {};
if (!fields) return;
Object.keys(fields).forEach((key) => {
const error = fields[key].error;
values[key] = fields[key].value;
if (error) errors[key] = fields[key].error;
});
if (Object.keys(errors).length) return;
onSubmit && onSubmit(values);
}
render() {
return (
<form onSubmit={this.handleSubmit}>
{ this.props.children }
</form>
);
}
}
const mapStateToProps = (state: ReduxState, ownProps: OwnProps): StateProps => {
return {
fields: state.forms[ownProps.id],
};
};
export default connect(mapStateToProps)(Form);
import React from 'react';
import { connect } from 'react-redux';
import { addField, removeField, setValue, setError } from 'ducks/forms/forms.actions';
class FormField extends React.Component {
componentDidMount() {
const { dispatch, formId, name, defaultValue } = this.props;
dispatch(addField({ formId, name, value: defaultValue }));
}
componentWillUnmount() {
const { dispatch, formId, name } = this.props;
dispatch(removeField({ formId, name }));
}
handleChange = async (value) => {
const { dispatch, formId, name, onChange } = this.props;
await dispatch(setValue({ formId, name, value }));
if (onChange) onChange(value);
}
handleBlur = () => {
const { form, formId, validators, name, dispatch, onBlur } = this.props;
if (onBlur) onBlur();
if (!validators || !validators.length) return;
const values = Object.keys(form).reduce((acc, key) => ({ ...acc, [key]: form[key].value }), {});
for (const index in validators) {
const validator = validators[index];
const error = validator(name, values);
if (error) {
dispatch(setError({ formId, name, error }));
return;
}
}
}
render() {
const { children, name, field, defaultValue } = this.props;
const error = field && field.error;
return (
<React.Fragment>
{
children({
onChange: this.handleChange,
onBlur: this.handleBlur,
value: defaultValue,
name,
error,
})
}
</React.Fragment>
);
}
}
const mapStateToProps = (state, ownProps) => {
const form = state.forms[ownProps.formId] || {};
const field = form[ownProps.name];
return { field, form };
};
export default connect(mapStateToProps)(FormField);
import actionTypes from './forms.actionTypes';
const initialState = {};
export default function (state = initialState, action) {
const { type, payload } = action;
switch (type) {
case actionTypes.ADD_FORM: {
const { formId } = payload;
if (state[formId]) return state;
return { ...state, [formId]: {} };
}
case actionTypes.REMOVE_FORM: {
const { formId } = payload;
const nextState = { ...state };
delete nextState[formId];
return state;
}
case actionTypes.ADD_FIELD: {
const { formId, name, value } = payload;
const nextState = { ...state };
if (!nextState[formId]) nextState[formId] = {};
nextState[formId][name] = { value };
return nextState;
}
case actionTypes.REMOVE_FIELD: {
const { formId, name } = payload;
if (!state[formId]) return state;
const nextState = { ...state };
delete nextState[formId][name];
return nextState;
}
case actionTypes.SET_VALUE: {
const { formId, name, value } = payload;
const nextState = { ...state };
nextState[formId][name] = { value, error: '' };
return nextState;
}
case actionTypes.SET_ERROR: {
const { formId, name, error } = payload;
return {
...state,
[formId]: {
...state[formId],
[name]: {
...state[formId][name],
error,
},
},
};
}
default:
return state;
}
}
export const required = (error = 'Field is required') => (name, values) => {
if (!values[name]) return error;
};
export const isEmail = (error = 'Please enter a valid email') => (name, values) => {
const regex = /(^$|^.+@.+\..+$)/;
const value = values[name];
if (value && !value.match(regex)) return error;
};
export const isSame = (comparedName, error = 'Invalid value') => (name, values) => {
if (values[comparedName] !== values[name]) return error;
};
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment