@ackee/mateus 中文文档教程
Mateus
Antd 组件作为表单字段 + redux-saga 流,以便更轻松地处理 redux-form 表单的提交。
Table of contents
Installation
使用 npm:
npm i -S @ackee/mateus
API
Action creators
submitForm(formId: string, submitActionCreator: () => object): function
formId
表单唯一标识符submitActionCreator
表单特定提交操作的操作创建者返回用于
handleSubmit
prop 的表单提交处理程序redux-form
Form
.示例 - 在
react-redux
容器中使用submitForm
```js 从'react-redux'导入{connect}; 从 'redux' 导入 { bindActionCreators, compose };
从'./components/UserForm'导入用户窗体;
常量 createUserAction = () => ({ 类型:'CREATE_USER' })
const formId = 'createUserForm';
导出默认撰写( 连接( (状态)=> ({ 初始值:{ }, }), 调度 => ({ onSubmit: bindActionCreators( formActions.submitForm(formId, createUserAction), 派遣, ), }), ), reduxForm({ 表格:表格编号, }), )(用户窗体);
```
Action types
FORM_SUBMIT
import { actionTypes } from '@ackee/mateus';
import { takeEvery } from 'redux-saga/effects';
takeEvery(actionTypes.FORM_SUBMIT, action => {
const { data, form } = action;
console.log('Submitted form', form);
});
Sagas
submitFormSaga(): void
Form saga 确保您在表单提交处理程序中收到所有必要的数据和方法(例如,用于处理表单流)。 只需将它用作您唯一的传奇,或在传奇列表中。
与表单提交传奇并行,您可能希望使用 submitForm
操作创建器。
示例 -
import { submitFormSaga as formSaga } from 'ackee-frontend-toolkit/sagas';
// main saga
export default function* () {
yield all([
// ... other sagas
formSaga(),
]);
}
Complete example of using form saga flow
在 redux-form
中使用我们的 submitForm saga 处理表单提交很容易,并且可以实现关注点分离。
这一切都分为几个简单的步骤
- Plug
submitFormSaga
into main saga - Use
submitForm
action as a handler of form submit - Catch action provided as a second parameter to
submitForm
and hadle it by custom saga. - Manage whole
redux-form
submit process, including startSubmit, stopSubmit and reset, in custom saga.
示例有点缩短和简化:
// config/index.js
export default {
// ...
forms: {
addUser: 'addUserForm',
}
api: {
user: '/api/v1/user',
}
}
// actions/user.js
export const addUserAction = () => ({
type: 'ADD_USER'
})
// components/UserForm.js
const UserForm = ({ handleSubmit, submitting }) => (
<Form onSubmit={handleSubmit}>
<Field
disabled={submitting}
id="firstname"
name="firstname"
component={Input}
type="text"
/>
<Field
disabled={submitting}
id="lastname"
name="lastname"
component={Input}
type="text"
/>
<button type="submit">Add user</button>
</Form>
);
UserForm.propTypes = {
// these props are automatically supplied by reduxForm()
handleSubmit: PropTypes.func.isRequired,
submitting: PropTypes.bool.isRequired,
invalid: PropTypes.bool.isRequired,
};
export default UserForm;
// containers/UserForm.js
import { submitForm } from '@ackee/mateus';
import { addUserAction } from '../actions/user';
import config from '../config';
export default compose(
connect(
(state) => ({
initialValues: {},
}),
dispatch => ({
onSubmit: bindActionCreators(
submitForm(config.forms.addUser, addUserAction),
dispatch,
),
}),
),
reduxForm({
form: config.forms.addUser,
}),
)(UserForm);
// sagas/index.js
import { submitFormSaga } from '@ackee/mateus';
import userSaga from '../sagas/userSaga';
export default function* () {
yield all([
submitFormSaga(),
userSaga(),
]);
}
// sagas/userSaga.js
import config from '../config';
function* handleAddUserForm(action) {
const { data, startSubmit, stopSubmit, reset } = action;
yield startSubmit();
try {
yield api.post(
config.api.user,
{
firstname: data.firstname,
lastname: data.lastname,
},
);
yield stopSubmit();
yield reset();
} catch (e) {
const errors = { 'user add error': e };
yield stopSubmit(errors);
}
}
export default function* () {
yield all([
takeEvery('ADD_USER', handleAddUserForm),
]);
}
Form fields
所有表单字段都可以作为 Antd 组件使用(例如 TextInput) 包裹着 FormItem 或相同,但包含在 redux-form 中 字段。
List of fields:
* DatePicker & TimePicker
Text
TextInput
接受 AntdInput
属性。TextField
接受与 Input 相同的道具以及所有 props 你可以传递给 redux-form 字段。import { TextInput, TextField } from '@ackee/mateus';
TextArea
TextAreaInput
接受 AntdTextArea
属性。TextAreaField
接受与 Input 相同的道具以及所有 props 你可以传递给 redux-form 字段。import { TextAreaInput, TextAreaField } from '@ackee/mateus';
Select
SelectInput
接受 AntdSelect
props,而不是将选项作为子组件传递,它们作为道具中的数组传递。该道具的默认名称是
options
,它的形状是{ label: ReactNode, value: string|number }
。 可以通过指定optionsKey
、labelKey
或valueKey
属性来更改名称(请看下面的示例)。SelectField
接受与 Input 相同的道具以及所有 props 你可以传递给 redux-form 字段。import { SelectInput, SelectField } from '@ackee/mateus'; const select = ( <SelectInput options={[ { label: 'Option1': value: 1 }, { label: <span>Option2</span>: value: 2 }, ]} /> ); const selectWithCustomNames = ( <SelectInput optionsKey="users" labelKey="name" valueKey="id" users={[ { name: 'Anakin', id: 'siths1' }, { name: 'Luke', id: 'jedis1' }, ]} /> );
NumberInput
NumberInput
接受 AntdInputNumber
属性。NumberField
接受与 Input 相同的道具加上所有 props 你可以传递给 redux-form 字段。import { NumberInput, NumberField } from '@ackee/mateus';
Switch
SwitchInput
接受 AntdSwitch
属性。SwitchField
接受与 Input 相同的道具以及所有 props 你可以传递给 redux-form 字段。import { SwitchInput, SwitchField } from '@ackee/mateus';
Slider
SliderInput
接受 AntdSlider
属性。SliderField
接受与 Input 相同的道具以及所有 props 你可以传递给 redux-form 字段。import { SliderInput, SliderField } from '@ackee/mateus';
Radio
这有点令人困惑,因为 RadioInput
实际上是 RadioGroup
- 在大多数情况下,我们想要呈现一组收音机让用户选择一个选项,所以我们不需要单独的收音机
。 如果出于任何原因你真的只需要一个没有 RadioGroup
的 Radio
就可以直接从 antd
使用它。
RadioInput
接受 AntdRadioGroup
props,但有几个不同之处- the definition
options
prop is a bit changed. It lacksdisabled
field so the shape is just{ label: ReactNode, value: string|number }
. - on the other hand, as same as for
Select
you can specify custom names for props and the shape keys by specifyingoptionsKey
,labelKey
orvalueKey
prop (look at example below). - you can pass
button
prop of type boolean that force use of radio buttons instead of plain radios.
- the definition
< code>RadioField 接受与 Input 相同的道具加上所有 props 你可以传递给 redux-form Field。
import { RadioInput, RadioField } from '@ackee/mateus'; const selectYourFavourite = ( <RadioInput optionsKey="southPark" labelKey="name" valueKey="color" southPark={[ { name: 'Kenny', color: 'orange' }, { name: 'Cartman', color: 'fat' }, { name: 'Stan', color: 'blue' }, { name: 'Kyle', color: 'green' }, ]} button /> );
CheckboxGroup
CheckboxGroupInput
接受 AntdCheckboxGroup
props。CheckboxGroupField
接受与 Input 相同的道具加上所有 props 你可以传递给 redux-form 字段。import { CheckboxGroupInput, CheckboxGroupField } from '@ackee/mateus';
DatePicker & TimePicker
两个选择器都接受来自 shared API 的 props 加上:
DatePickerInput
accept AntdDatePicker
props.TimePickerInput
accept AntdTimePicker
props.- both accept
displayFormat
prop which is the same asformat
prop defined in picker's API. But since redux-formField
has alsoformat
prop and so Input's one would be overriden when usingDatePickerField
orTimePickerField
, wee need to provide an alternative.
DatePickerField
/TimePickerField
接受与DatePickerInput
/TimePickerInput
相同的道具加上所有 props 可以传递给 redux-form 字段。import { DatePickerInput, DatePickerField, TimePickerInput, TimePickerField } from '@ackee/mateus';
pickerEnhancer
TBD
import { pickerEnhancer } from '@ackee/mateus';
HOC
wrapWithField(component: React.Component, injectProps?: InjectPropsType): React.Component
高阶组件,用 redux-form 的 Field
包装提供的组件并向其中注入额外的属性(参见 InjectPropsType
)。 这对于不属于 @ackee/mateus
的表单字段很有用。
InjectPropsType
是一个 propsobject
或函数(props: object): object
接收传递给组件的 props 并返回额外的 props传递给组件示例 - 将它用于 Antd
Rate
组件const Rate = wrapWithField( AntdRate, { character: <Icon type="heart" /> } ); const RateForm = ({ handleSubmit, invalid }) => ( <Form onSubmit={handleSubmit}> <Rate /> </Form> );
Mateus
Antd components as a form fields + redux-saga flow for easier handle of redux-form form's submit.
Name of package Mateus refers to name of the apostole Matthew which is a patron of all office workers. Mateus is a Portuguese equivalent for Matthew.
Table of contents
Installation
Using npm:
npm i -S @ackee/mateus
API
Action creators
submitForm(formId: string, submitActionCreator: () => object): function
formId
Form unique identificatorsubmitActionCreator
Action creator for form specific submit actionReturns Form submit handler that is used for
handleSubmit
prop ofredux-form
Form
.Example - use
submitForm
inreact-redux
container```js import { connect } from 'react-redux'; import { bindActionCreators, compose } from 'redux';
import UserForm from './components/UserForm';
const createUserAction = () => ({ type: 'CREATE_USER' })
const formId = 'createUserForm';
export default compose( connect( (state) => ({ initialValues: { }, }), dispatch => ({ onSubmit: bindActionCreators( formActions.submitForm(formId, createUserAction), dispatch, ), }), ), reduxForm({ form: formId, }), )(UserForm);
```
Action types
FORM_SUBMIT
import { actionTypes } from '@ackee/mateus';
import { takeEvery } from 'redux-saga/effects';
takeEvery(actionTypes.FORM_SUBMIT, action => {
const { data, form } = action;
console.log('Submitted form', form);
});
Sagas
submitFormSaga(): void
Form saga ensures you receive all neccessary data and methods (eg. for handling form flow) in form submit handler. Just use it as your only saga, or in list of sagas.
In parallel with form submit saga, you will likely want to use submitForm
action creator.
Examples - Usage
import { submitFormSaga as formSaga } from 'ackee-frontend-toolkit/sagas';
// main saga
export default function* () {
yield all([
// ... other sagas
formSaga(),
]);
}
Complete example of using form saga flow
Handling form submit using our submitForm saga in redux-form
is easy and enable separation of concerns.
It's all divided into few simple steps
- Plug
submitFormSaga
into main saga - Use
submitForm
action as a handler of form submit - Catch action provided as a second parameter to
submitForm
and hadle it by custom saga. - Manage whole
redux-form
submit process, including startSubmit, stopSubmit and reset, in custom saga.
Example is a bit shortened and simplified:
// config/index.js
export default {
// ...
forms: {
addUser: 'addUserForm',
}
api: {
user: '/api/v1/user',
}
}
// actions/user.js
export const addUserAction = () => ({
type: 'ADD_USER'
})
// components/UserForm.js
const UserForm = ({ handleSubmit, submitting }) => (
<Form onSubmit={handleSubmit}>
<Field
disabled={submitting}
id="firstname"
name="firstname"
component={Input}
type="text"
/>
<Field
disabled={submitting}
id="lastname"
name="lastname"
component={Input}
type="text"
/>
<button type="submit">Add user</button>
</Form>
);
UserForm.propTypes = {
// these props are automatically supplied by reduxForm()
handleSubmit: PropTypes.func.isRequired,
submitting: PropTypes.bool.isRequired,
invalid: PropTypes.bool.isRequired,
};
export default UserForm;
// containers/UserForm.js
import { submitForm } from '@ackee/mateus';
import { addUserAction } from '../actions/user';
import config from '../config';
export default compose(
connect(
(state) => ({
initialValues: {},
}),
dispatch => ({
onSubmit: bindActionCreators(
submitForm(config.forms.addUser, addUserAction),
dispatch,
),
}),
),
reduxForm({
form: config.forms.addUser,
}),
)(UserForm);
// sagas/index.js
import { submitFormSaga } from '@ackee/mateus';
import userSaga from '../sagas/userSaga';
export default function* () {
yield all([
submitFormSaga(),
userSaga(),
]);
}
// sagas/userSaga.js
import config from '../config';
function* handleAddUserForm(action) {
const { data, startSubmit, stopSubmit, reset } = action;
yield startSubmit();
try {
yield api.post(
config.api.user,
{
firstname: data.firstname,
lastname: data.lastname,
},
);
yield stopSubmit();
yield reset();
} catch (e) {
const errors = { 'user add error': e };
yield stopSubmit(errors);
}
}
export default function* () {
yield all([
takeEvery('ADD_USER', handleAddUserForm),
]);
}
Form fields
All form fields are available either as an Antd component (eg. TextInput) wrapped with FormItem or the same, but enclosed into redux-form Field.
List of fields:
* DatePicker & TimePicker
Text
TextInput
accept AntdInput
props.TextField
accept same props as Input plus all the props you can pass to redux-form Field.import { TextInput, TextField } from '@ackee/mateus';
TextArea
TextAreaInput
accept AntdTextArea
props.TextAreaField
accept same props as Input plus all the props you can pass to redux-form Field.import { TextAreaInput, TextAreaField } from '@ackee/mateus';
Select
SelectInput
accept AntdSelect
props, but instead of passing options as children components, they'are passed as an array in props.Default name for that prop is
options
and it's shape is{ label: ReactNode, value: string|number }
. The names can be changed by specifyingoptionsKey
,labelKey
orvalueKey
prop (look at example below).SelectField
accept same props as Input plus all the props you can pass to redux-form Field.import { SelectInput, SelectField } from '@ackee/mateus'; const select = ( <SelectInput options={[ { label: 'Option1': value: 1 }, { label: <span>Option2</span>: value: 2 }, ]} /> ); const selectWithCustomNames = ( <SelectInput optionsKey="users" labelKey="name" valueKey="id" users={[ { name: 'Anakin', id: 'siths1' }, { name: 'Luke', id: 'jedis1' }, ]} /> );
NumberInput
NumberInput
accept AntdInputNumber
props.NumberField
accept same props as Input plus all the props you can pass to redux-form Field.import { NumberInput, NumberField } from '@ackee/mateus';
Switch
SwitchInput
accept AntdSwitch
props.SwitchField
accept same props as Input plus all the props you can pass to redux-form Field.import { SwitchInput, SwitchField } from '@ackee/mateus';
Slider
SliderInput
accept AntdSlider
props.SliderField
accept same props as Input plus all the props you can pass to redux-form Field.import { SliderInput, SliderField } from '@ackee/mateus';
Radio
This is a bit confusing, because RadioInput
is actually RadioGroup
- in most cases we want to render group of radios to let user select one option so we don't need an individual Radio
. If from any reason you realy need only one Radio
without RadioGroup
just feel free to use it directly from antd
.
RadioInput
accept AntdRadioGroup
props, but with several differencies- the definition
options
prop is a bit changed. It lacksdisabled
field so the shape is just{ label: ReactNode, value: string|number }
. - on the other hand, as same as for
Select
you can specify custom names for props and the shape keys by specifyingoptionsKey
,labelKey
orvalueKey
prop (look at example below). - you can pass
button
prop of type boolean that force use of radio buttons instead of plain radios.
- the definition
RadioField
accept same props as Input plus all the props you can pass to redux-form Field.import { RadioInput, RadioField } from '@ackee/mateus'; const selectYourFavourite = ( <RadioInput optionsKey="southPark" labelKey="name" valueKey="color" southPark={[ { name: 'Kenny', color: 'orange' }, { name: 'Cartman', color: 'fat' }, { name: 'Stan', color: 'blue' }, { name: 'Kyle', color: 'green' }, ]} button /> );
CheckboxGroup
CheckboxGroupInput
accept AntdCheckboxGroup
props.CheckboxGroupField
accept same props as Input plus all the props you can pass to redux-form Field.import { CheckboxGroupInput, CheckboxGroupField } from '@ackee/mateus';
DatePicker & TimePicker
Both pickers accept props from shared API plus:
DatePickerInput
accept AntdDatePicker
props.TimePickerInput
accept AntdTimePicker
props.- both accept
displayFormat
prop which is the same asformat
prop defined in picker's API. But since redux-formField
has alsoformat
prop and so Input's one would be overriden when usingDatePickerField
orTimePickerField
, wee need to provide an alternative.
DatePickerField
/TimePickerField
accept same props asDatePickerInput
/TimePickerInput
plus all the props you can pass to redux-form Field.import { DatePickerInput, DatePickerField, TimePickerInput, TimePickerField } from '@ackee/mateus';
pickerEnhancer
TBD
import { pickerEnhancer } from '@ackee/mateus';
HOC
wrapWithField(component: React.Component, injectProps?: InjectPropsType): React.Component
Hight order component that wrap supplied component with redux-form's Field
and inject extra properties into it (see InjectPropsType
). This is useful for form fields that are not part of @ackee/mateus
.
InjectPropsType
is either an propsobject
or function(props: object): object
that receives props passed to component and returns extra props that will be also passed to componentExample - use it fo Antd
Rate
componentconst Rate = wrapWithField( AntdRate, { character: <Icon type="heart" /> } ); const RateForm = ({ handleSubmit, invalid }) => ( <Form onSubmit={handleSubmit}> <Rate /> </Form> );