代码之家  ›  专栏  ›  技术社区  ›  CHY

为什么我不能从事件处理程序函数调用Redux操作创建者,但可以从componentWillMount()调用?

  •  0
  • CHY  · 技术社区  · 8 年前

    我正在使用React、Redux和TypeScript开发一个应用程序。

    我有一个Redux存储设置,可以设置初始状态并成功填充我的组件。我现在正在尝试连接一个表单,该表单将调用我的动作创建者,该创建者使用过滤器值从控制器检索数据。

    我正在使用connect通过道具访问动作创建者。

    我可以从componentWillMount()调用我的动作创建者,但如果在表单提交处理程序函数中进行相同的调用,则会出现以下错误:

    未捕获的TypeError:无法读取未定义的属性“requestVersions”

    这是我的支持Redux的组件:

    import * as React from 'react';
    import { Link, NavLink, RouteComponentProps } from 'react-router-dom';
    import { connect } from 'react-redux';
    import { ApplicationState } from '../../store';
    import * as ASVersionQueueState from '../../store/ASVersionQueue';
    import { AgGridReact } from 'ag-grid-react';
    import SimpleGridExample from "./SimpleGridExample";
    import ASVersionQueueBulkActions from "./ASVersionQueueBulkActions";
    import ASVersionQueueFilters from "./ASVersionQueueFilters";
    import DataGridStats from "../common/DataGridStats";
    import PageTitleHeader from "../common/PageTitleHeader";
    
    type ASVersionQueueProps =
        ASVersionQueueState.ASVersionQueueState             // ... state we've requested from the Redux store
        & typeof ASVersionQueueState.actionCreators       // ... plus action creators we've requested
        & RouteComponentProps<{}>;                      // ... plus incoming routing parameters
    
    class ASVersionQueue extends React.Component<ASVersionQueueProps, {}> {
    
        handleChange(event: any) {
            console.log('Bulk Action: ' + event.target.value);
            console.log()
        }
    
        componentWillMount() {
            // This method runs when the component is first added to the page
            // This function call works
            this.props.requestVersions(this.props.versionQueueFilter);
            this.props.requestEmployeesList();
            this.props.requestEventsList();
        }
    
        handleEventsFilterChange(event: any) {
            console.log('Event Filter: ' + event.target.value);
        }
    
        handleFilterSubmission() { 
            // I removed my incoming parameters and am simply passing back the current state values to understand the source of my error
            //This function call is throwing the error
            this.props.requestVersions(this.props.versionQueueFilter);
        }
    
        public render() {
            return <div className="container-fluid">
                <PageTitleHeader
                    title="Account Services"
                    className="page-title"
                />
                <div className="as-grid grid-controls-container row">
                    <div className="col-lg-2 bulk-actions-container">
                        <ASVersionQueueBulkActions
                            outerClassName=""
                            controlClassName=""
                            htmlId="ver-grid-bulk-action"
                            htmlName="ver-grid-bulk-action"
                            onChangeFunction={() => (this.handleChange)}
    
                        // TO DO: pass down menu options to trigger Redux actions
    
                        />
                    </div>
                    <div className="col-lg-8 grid-filter-container">
                        <ASVersionQueueFilters 
                            outerClassName=""
                            htmlId=""
                            filterVals={this.props.versionQueueFilter}
                            employeesFilterList={this.props.employeesFilterList}
                            eventsFilterList={this.props.eventsFilterList}
                            onSubmitFunction={this.handleFilterSubmission}
                        />
                    </div>
                    <div className="col-lg-2 grid-stats-container">
                        <DataGridStats />
                    </div>
                </div>
                <div className="ag-grid full row ag-theme-blue">
                    <SimpleGridExample />
                </div>
            </div>;
        }
    }
    
    // Wire up the React component to the Redux store
    export default connect(
        (state: ApplicationState) => state.asVersionQueue,     // Selects which state properties are merged into the component's props
        ASVersionQueueState.actionCreators                 // Selects which action creators are merged into the component's props
    )(ASVersionQueue) as typeof ASVersionQueue;
    

    这是我的Redux操作创建者和还原者:

    import { fetch, addTask } from 'domain-task';
    import { Action, Reducer, ActionCreator } from 'redux';
    import { AppThunkAction } from './';
    import * as moment from 'moment';
    
    // -----------------
    // STATE - This defines the type of data maintained in the Redux store.
    
    export interface ASVersionQueueState {
        queuedVersions: QueuedVersion[];
        versionQueueFilter: VersionQueueFilter;
        eventsFilterList: SelectListItem[];
        employeesFilterList: SelectListItem[];
    }
    
    export interface QueuedVersion {
        VersionCode: string;
        VersionQualifier: string;
        VersionID: string;
        ProductID: string;
        PieceName: string;
        PrintClass: string;
        FirstInhomeDate: string;
        AccountID: string;
        AccountExecutive: string;
        AccountManager: string;
        ArtManager: string;
        AdUID: string;
        Status: string;
        Queue: string;
        DueDateOverride: string;
        IsLocked: string;
    }
    
    export interface VersionQueueFilter {
        StartDate: string;
        EndDate: string;
        PieceType: Array<string>;
        EventType: Array<string>;
        EventID: string;
        Employee: string;
    }
    
    export interface SelectListItem {
        OptionName: string;
        OptionVal: string;
    }
    
    let DefaultVersionQueueFilter = {
        StartDate: moment().subtract(30, 'days').format('YYYY-MM-DD'),
        EndDate: moment().format('YYYY-MM-DD'),
        PieceType: ['pt-impactpc'],
        EventType: ['et-special'],
        EventID: '',
        Employee: '12345'
    }
    
    
    // -----------------
    // ACTIONS - These are serializable (hence replayable) descriptions of state transitions.
    // They do not themselves have any side-effects; they just describe something that is going to happen.
    // Use @typeName and isActionType for type detection that works even after serialization/deserialization.
    
    interface RequestVersionsAction {
        type: 'REQUEST_VERSIONS';
        versionQueueFilter: VersionQueueFilter;
    }
    
    interface ReceiveVersionsAction {
        type: 'RECEIVE_VERSIONS';
        versionQueueFilter: VersionQueueFilter;
        receivedVersions: QueuedVersion[];
    }
    
    interface RequestEmployeesListAction {
        type: 'REQUEST_EMPLOYEES_LIST';
    }
    
    interface ReceiveEmployeesListAction {
        type: 'RECEIVE_EMPLOYEES_LIST';
        receivedEmployeesList: SelectListItem[];
    }
    
    interface RequestEventsListAction {
        type: 'REQUEST_EVENTS_LIST';
    }
    
    interface ReceiveEventsListAction {
        type: 'RECEIVE_EVENTS_LIST';
        receivedEventsList: SelectListItem[];
    }
    
    
    // Declare a 'discriminated union' type. This guarantees that all references to 'type' properties contain one of the
    // declared type strings (and not any other arbitrary string).
    type KnownAction = RequestVersionsAction | ReceiveVersionsAction | RequestEmployeesListAction | ReceiveEmployeesListAction | RequestEventsListAction | ReceiveEventsListAction;
    
    // ----------------
    // ACTION CREATORS - These are functions exposed to UI components that will trigger a state transition.
    // They don't directly mutate state, but they can have external side-effects (such as loading data).
    
    export const actionCreators = {
        requestVersions: (versionQueueFilter: VersionQueueFilter): AppThunkAction<KnownAction> => (dispatch, getState) => {
            console.log('actionCreator requestVersions called...');
            if (versionQueueFilter !== getState().asVersionQueue.versionQueueFilter) {
                let fetchTask = fetch(`api/Versions`)
                    .then(response => response.json() as Promise<QueuedVersion[]>)
                    .then(data => {
                        dispatch({ type: 'RECEIVE_VERSIONS', versionQueueFilter: versionQueueFilter, receivedVersions: data });
                        //dispatch({ type: 'RECEIVE_VERSIONS', receivedVersions: data });
                    });
    
                addTask(fetchTask); // Ensure server-side prerendering waits for this to complete
                dispatch({ type: 'REQUEST_VERSIONS', versionQueueFilter: versionQueueFilter });
                //dispatch({ type: 'REQUEST_VERSIONS' });
            }
        },
        requestEmployeesList: (): AppThunkAction<KnownAction> => (dispatch, getState) => {
            let fetchTask = fetch(`api/EmployeesList`)
                .then(response => response.json() as Promise<SelectListItem[]>)
                .then(data => {
                    dispatch({ type: 'RECEIVE_EMPLOYEES_LIST', receivedEmployeesList: data });
                });
    
            addTask(fetchTask); // Ensure server-side prerendering waits for this to complete
            dispatch({ type: 'REQUEST_EMPLOYEES_LIST' });
        },
        requestEventsList: (): AppThunkAction<KnownAction> => (dispatch, getState) => {
            let fetchTask = fetch(`api/EventsLIst`)
                .then(response => response.json() as Promise<SelectListItem[]>)
                .then(data => {
                    dispatch({ type: 'RECEIVE_EVENTS_LIST', receivedEventsList: data });
                });
    
            addTask(fetchTask); // Ensure server-side prerendering waits for this to complete
            dispatch({ type: 'REQUEST_EVENTS_LIST' });
        }
    };
    
    // ----------------
    // REDUCER - For a given state and action, returns the new state. To support time travel, this must not mutate the old state.
    const unloadedState: ASVersionQueueState = { queuedVersions: [], versionQueueFilter: DefaultVersionQueueFilter, eventsFilterList: [], employeesFilterList: [] };
    
    export const reducer: Reducer<ASVersionQueueState> = (state: ASVersionQueueState, incomingAction: Action) => {
        const action = incomingAction as KnownAction;
        switch (action.type) {
            case 'REQUEST_EMPLOYEES_LIST':
                return {
                    queuedVersions: state.queuedVersions,
                    versionQueueFilter: state.versionQueueFilter,
                    eventsFilterList: state.eventsFilterList,
                    employeesFilterList: state.employeesFilterList
                }
            case 'REQUEST_EVENTS_LIST':
                return {
                    queuedVersions: state.queuedVersions,
                    versionQueueFilter: state.versionQueueFilter,
                    eventsFilterList: state.eventsFilterList,
                    employeesFilterList: state.employeesFilterList
                }
            case 'REQUEST_VERSIONS':
                return {
                    queuedVersions: state.queuedVersions,
                    versionQueueFilter: state.versionQueueFilter,
                    eventsFilterList: state.eventsFilterList,
                    employeesFilterList: state.employeesFilterList
                }
            case 'RECEIVE_EMPLOYEES_LIST':
                return {
                    queuedVersions: state.queuedVersions,
                    versionQueueFilter: state.versionQueueFilter,
                    eventsFilterList: state.eventsFilterList,
                    employeesFilterList: action.receivedEmployeesList
                }
            case 'RECEIVE_EVENTS_LIST':
                return {
                    queuedVersions: state.queuedVersions,
                    versionQueueFilter: state.versionQueueFilter,
                    eventsFilterList: action.receivedEventsList,
                    employeesFilterList: state.employeesFilterList
                }
            case 'RECEIVE_VERSIONS':
                // Only accept the incoming data if it matches the most recent request. This ensures we correctly
                // handle out-of-order responses.
                if (action.versionQueueFilter === state.versionQueueFilter) {
                    return {
                        queuedVersions: action.receivedVersions,
                        versionQueueFilter: action.versionQueueFilter,
                        eventsFilterList: state.eventsFilterList,
                        employeesFilterList: state.employeesFilterList
                    };
                }
                break;
            default:
                // The following line guarantees that every action in the KnownAction union has been covered by a case above
                const exhaustiveCheck: never = action;
        }
    
        return state || unloadedState;
    };
    

    在此处调用我的动作创建者有效:

    componentWillMount() {
            this.props.requestVersions(this.props.versionQueueFilter);
        }
    

    在此处调用我的操作创建者会导致错误,我不明白原因:

    handleFilterSubmission() { 
            this.props.requestVersions(this.props.versionQueueFilter);
        }
    

    注意:我删除了提交处理程序上的函数参数,只是为了确认所传递内容的数据类型没有问题。

    我从一个子组件调用该函数,我将引用传递给该函数。

    这是父组件中的子组件引用:

    <ASVersionQueueFilters 
        outerClassName=""
        htmlId=""
        filterVals={this.props.versionQueueFilter}
        employeesFilterList={this.props.employeesFilterList}
        eventsFilterList={this.props.eventsFilterList}
        onSubmitFunction={this.handleFilterSubmission}
    />
    

    在子组件中,我已将传递的函数“onSubmitFunction”绑定到最终调用onSubmitFunction的事件处理程序函数:

    handleFormSubmit(event: React.FormEvent<EventTarget>) {
        event.preventDefault;
        let filterVals = {
            StartDate: moment(this.state.startDate).format('YYYY-MM-DD'),
            EndDate: moment(this.state.endDate).format('YYYY-MM-DD'),
            PieceType: [],
            EventType: [],
            EventID: this.state.selectedEventID,
            Employee: this.state.selectedEmployee
        }
        this.props.onSubmitFunction(filterVals);
    }
    

    该按钮启动该函数,我可以在控制台中看到筛选器值已成功传递回父函数,但以这种方式调用我的操作创建者会不断抛出错误。

    知道我做错了什么吗?

    1 回复  |  直到 8 年前
        1
  •  1
  •   Tomek Rychtyk    8 年前

    你需要把你的 handleFilterSubmission 具有 this . 为此,您可以创建如下构造函数:

    constructor(props) {
        super(props);
    
        this.handleFilterSubmission = this.handleFilterSubmission.bind(this);
    }