如何使反应组件与Redux流中可重用的异步数据一起使用?

时间:2017-03-13 06:24:43

标签: javascript reactjs redux react-redux

它更像是一个建筑问题,而不是技术问题。我正在开发具有以下结构的应用程序(Standard Redux)。

UserFormContainer-> UserForm->(Fields,RoleField,Group Field)。现在我的所有字段都是哑组件,但角色和组除外,它们依赖于要填充的服务器的数据。根据标准redux,我的状态有单向流,所有数据都通过主容器,哑组件只调度动作。

我的问题是,由于角色和组字段依赖于它的数据的父组件,我如何使这些可重用?什么是最佳做法?

这是我的基本组件的单向流程。

enter image description here

2 个答案:

答案 0 :(得分:1)

package main import ( "fmt" "gopkg.in/olivere/elastic.v5" "strconv" ) type Tweet struct { User string `json:"user"` Message string `json:"message"` } func main() { client, err := elastic.NewClient() if err != nil { fmt.Println("%v", err) } n := 0 for i := 0; i < 1000; i++ { bulkRequest := client.Bulk() for j := 0; j < 10000; j++ { n++ tweet := Tweet{User: "olivere", Message: "Package strconv implements conversions to and from string representations of basic data types. " + strconv.Itoa(n)} req := elastic.NewBulkIndexRequest().Index("twitter").Type("tweet").Id(strconv.Itoa(n)).Doc(tweet) bulkRequest = bulkRequest.Add(req) } bulkResponse, err := bulkRequest.Do() if err != nil { fmt.Println(err) } if bulkResponse != nil { } fmt.Println(i) } } 应该是一个愚蠢的组件,它从RoleField组件获取数据。 UserForm应该从商店获取数据并将其作为道具传递给UserForm。这种方式RoleField将是可重用的,因为它们可以显示从父组件传递给它们的数据。

<强> UDPATE

您还可以使RoleField包装容器可以接受自定义表单字段作为子容器。请参阅下面的示例代码

UserForm

答案 1 :(得分:1)

当你有几个嵌套组件时,@DeividasKaržinauskas的答案非常有意义,我认为这是合理的方式,但是因为我希望我的组件完全独立于任何其他组件,但是可以测试不包含任何数据逻辑我只是想出了稍微不同的方法。当然DeividasKaržinauskas的答案有所帮助。

为了使我的组件完全独立,我只是将它作为另一个容器,所有副作用都由动作/动作创建者处理,并且它仍然是可测试的,而数据是由它自己的独立减速器计算的。然后我使用redux将状态作为道具传递。

代码示例

import React from 'react';
import {bindActionCreators} from 'redux';
import {connect} from 'react-redux';

class RolesField extends React.Component {

    componentWillMount() {
        //if not fetched then fetch
        this.props.fetchRoles();
    }

    roles() {
        let {roles} = this.props;
        if (roles.length > 0) {
            const {input, label, placeholder, type, meta: {touched, error, warning}} = this.props;
            let rolesUi = roles.map((role, index) => {
                return (
                    <div className="checkbox" key={index}>
                        <label htmlFor={input.name}>
                            <input {...input} placeholder={placeholder} type={type}/>
                            &nbsp;{role.name} <a href="#">( Show Permissions )</a>
                        </label>
                    </div>
                );
            });

            return rolesUi;
        }
    }


    render() {

        const {input, label, placeholder, type, meta: {touched, error, warning}} = this.props;

        return (
            <div className="form-group">
                <label className="col-sm-2 control-label" htmlFor={input.name}>
                    Administrative Roles
                </label>
                <div className="col-sm-10">
                    {/*This section should be loaded from server,
                     the component should dispatch the event and role action should load the roles.*/}
                    {this.roles()}
                </div>
            </div>
        );
    }

}

//map state to props.
function mapStateToProps(state) {
    return {
        roles: state.roles.roles
    }
}

function mapDispatchToProps(dispatch) {
    let actionCreators = {
        fetchRoles
    };

    return bindActionCreators(actionCreators, dispatch);

}

export default connect(mapStateToProps, mapDispatchToProps)(RolesField);

/*Action Types*/
const ROLE_FETCH_START = 'ROLE_FETCH_START';
const ROLE_FETCH_COMPLETE = 'ROLE_FETCH_COMPLETE';
/*End Action Types*/

/**
 * Action Creator
 * @returns {{type: string, payload: [*,*,*,*]}}
 */
function fetchRoles() {
    const roles = [
        {'id': 1, 'name': 'Super Admin'},
        {'id': 2, 'name': 'Admin'},
        {'id': 3, 'name': 'Manager'},
        {'id': 4, 'name': 'Technical Lead'}
    ];
    /*Action*/
    return {
        type: ROLE_FETCH_COMPLETE,
        payload: roles
    };
}
/**
 * Roles Reducer
 * @param state
 * @param action
 * @returns {*}
 */
export function rolesReducer(state = {roles: [], fetched: false}, action) {
    switch (action.type) {
        case ROLE_FETCH_COMPLETE :
            return {
                ...state,
                fetched: true,
                roles: action.payload
            };
            break;
    }

    return state;
}

要重新使用redux-form中的组件,我做了

import React from 'react';
import {Field, reduxForm} from 'redux-form';

import {renderInputField, renderDropdownList} from '../../page/components/field.jsx';
import RolesField from '../../page/containers/fields/roles.jsx';

import Box from '../../page/components/box.jsx';
import ActionButtons from '../../page/components/action-buttons';

class UserForm extends React.Component {

    actionButtons() {
        return [
            {
                'type' : 'submit',
                'label' : 'Save',
                'className' : 'btn-primary'
            },
            {
                'type' : 'button',
                'label' : 'Cancel',
                'className' : 'btn-success',
                'onClick' : this.props.reset
            },
            {
                'type' : 'button',
                'label' : 'Delete',
                'className' : 'btn-danger'
            }
        ]
    }

    render() {
        const { handleSubmit } = this.props;
        return (
            <form className="form-horizontal" role="form" onSubmit={handleSubmit}>
                <Box title="Add New User">
                    <ActionButtons buttons = {this.actionButtons()} />
                    <Field
                        name="roles[]"
                        component={RolesField}
                        label="Technical Lead"
                        type="checkbox"
                        className="form-control" />

                </Box>

            </form>
        );
    }

}

UserForm = reduxForm({
    form: 'userForm',
    validate
})(UserForm);

export default UserForm;

这是修改后的图表。我不确定这是最佳做法还是有任何与之相关的缺点,但它只适用于我的用例。

状态图表

component state chart

图表的道歉不太清楚。

Reusable Redux React Component