@ackee/mateus 中文文档教程

发布于 4年前 浏览 21 项目主页 更新于 3年前

ackee|Mateus

GitHub licenseCI StatusPRs WelcomeDependency Status

Mateus

Antd 组件作为表单字段 + redux-saga 流,以便更轻松地处理 redux-form 表单的提交。

包裹名称 Mateus 是指使徒马修的名字,他是所有上班族的守护神. Mateus 是 Matthew 的葡萄牙语

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 处理表单提交很容易,并且可以实现关注点分离。

这一切都分为几个简单的步骤

  1. Plug submitFormSaga into main saga
  2. Use submitForm action as a handler of form submit
  3. Catch action provided as a second parameter to submitForm and hadle it by custom saga.
  4. 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 接受 Antd Input 属性

  • TextField 接受与 Input 相同的道具以及所有 props 你可以传递给 redux-form 字段。

    import { TextInput, TextField } from '@ackee/mateus';
    

TextArea

  • TextAreaInput 接受 Antd TextArea 属性

  • TextAreaField 接受与 Input 相同的道具以及所有 props 你可以传递给 redux-form 字段。

    import { TextAreaInput, TextAreaField } from '@ackee/mateus';
    

Select

  • SelectInput 接受 Antd Select props,而不是将选项作为子组件传递,它们作为道具中的数组传递。

    该道具的默认名称是 options,它的形状是 { label: ReactNode, value: string|number }。 可以通过指定 optionsKeylabelKeyvalueKey 属性来更改名称(请看下面的示例)。

  • 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 接受 Antd InputNumber 属性

  • NumberField 接受与 Input 相同的道具加上所有 props 你可以传递给 redux-form 字段。

    import { NumberInput, NumberField } from '@ackee/mateus';
    

Switch

  • SwitchInput 接受 Antd Switch 属性

  • SwitchField 接受与 Input 相同的道具以及所有 props 你可以传递给 redux-form 字段。

    import { SwitchInput, SwitchField } from '@ackee/mateus';
    

Slider

  • SliderInput 接受 Antd Slider 属性

  • SliderField 接受与 Input 相同的道具以及所有 props 你可以传递给 redux-form 字段。

    import { SliderInput, SliderField } from '@ackee/mateus';
    

Radio

这有点令人困惑,因为 RadioInput 实际上是 RadioGroup - 在大多数情况下,我们想要呈现一组收音机让用户选择一个选项,所以我们不需要单独的收音机。 如果出于任何原因你真的只需要一个没有 RadioGroupRadio 就可以直接从 antd 使用它。

  • RadioInput 接受 Antd RadioGroup props,但有几个不同之处

    • the definition options prop is a bit changed. It lacks disabled 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 specifying optionsKey, labelKey or valueKey prop (look at example below).
    • you can pass button prop of type boolean that force use of radio buttons instead of plain radios.
  • < 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 接受 Antd CheckboxGroup props

  • CheckboxGroupField 接受与 Input 相同的道具加上所有 props 你可以传递给 redux-form 字段。

    import { CheckboxGroupInput, CheckboxGroupField } from '@ackee/mateus';
    

DatePicker & TimePicker

  • 两个选择器都接受来自 shared API 的 props 加上:

    • DatePickerInput accept Antd DatePicker props.
    • TimePickerInput accept Antd TimePicker props.
    • both accept displayFormat prop which is the same as format prop defined in picker's API. But since redux-form Field has also format prop and so Input's one would be overriden when using DatePickerField or TimePickerField, 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 是一个 props object 或函数 (props: object): object 接收传递给组件的 props 并返回额外的 props传递给组件

    示例 - 将它用于 Antd Rate 组件

    const Rate = wrapWithField(
        AntdRate,
        {
            character: <Icon type="heart" />
        }
    );
    
    const RateForm = ({ handleSubmit, invalid }) => (
        <Form onSubmit={handleSubmit}>
            <Rate />
        </Form>
    );
    

ackee|Mateus

GitHub licenseCI StatusPRs WelcomeDependency Status

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 identificator

  • submitActionCreator Action creator for form specific submit action

  • Returns Form submit handler that is used for handleSubmit prop of redux-form Form.

    Example - use submitForm in react-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

  1. Plug submitFormSaga into main saga
  2. Use submitForm action as a handler of form submit
  3. Catch action provided as a second parameter to submitForm and hadle it by custom saga.
  4. 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 Antd Input 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 Antd TextArea 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 Antd Select 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 specifying optionsKey, labelKey or valueKey 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 Antd InputNumber 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 Antd Switch 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 Antd Slider 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 Antd RadioGroup props, but with several differencies

    • the definition options prop is a bit changed. It lacks disabled 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 specifying optionsKey, labelKey or valueKey prop (look at example below).
    • you can pass button prop of type boolean that force use of radio buttons instead of plain radios.
  • 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 Antd CheckboxGroup 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 Antd DatePicker props.
    • TimePickerInput accept Antd TimePicker props.
    • both accept displayFormat prop which is the same as format prop defined in picker's API. But since redux-form Field has also format prop and so Input's one would be overriden when using DatePickerField or TimePickerField, wee need to provide an alternative.
  • DatePickerField/TimePickerField accept same props as DatePickerInput/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 props object or function (props: object): object that receives props passed to component and returns extra props that will be also passed to component

    Example - use it fo Antd Rate component

    const Rate = wrapWithField(
        AntdRate,
        {
            character: <Icon type="heart" />
        }
    );
    
    const RateForm = ({ handleSubmit, invalid }) => (
        <Form onSubmit={handleSubmit}>
            <Rate />
        </Form>
    );
    
    我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
    原文