在componentDidMount()中进行反应调度操作

时间:2017-09-16 19:42:55

标签: javascript reactjs redux react-redux

我一直试图在componentDidMount中发送一个动作,不幸的是我得到了:

超出最大调用堆栈大小

import React, { Component } from 'react';
import ReactDOM from 'react-dom';
import { connect } from 'react-redux';
import PropTypes from 'prop-types';
import moment from 'moment';

import styles from './style.css';
import Arrow from './components/Arrow';
import RadioCheck from 'components/RadioCheck';
import Select from 'components/Select';
import DatePickerWrapper from 'components/DatePickerWrapper';
import { months } from 'utils';
import { changeMonth, changeFromDate, changeToDate, changeRadioDatepicker } from 'components/PostsPage/actions';

class DatePickerDropdown extends Component {
    constructor(props) {
        super(props);
        this.firstChild = null;

        this.state = {
            open: false,
            loadedPreferences: false,
        };

        this._handleSelectedClick = this._handleSelectedClick.bind(this);
        this._handleRadioChange = this._handleRadioChange.bind(this);
        this._handleFromDatepickerChange = this._handleFromDatepickerChange.bind(this);
        this._handleToDatepickerChange = this._handleToDatepickerChange.bind(this);
        this._handleMonthChange = this._handleMonthChange.bind(this);
        this._handleOutsideClick = this._handleOutsideClick.bind(this);

    }

    componentDidMount() {
        window.addEventListener('click', this._handleOutsideClick, false);
        setTimeout(() => {
            this.loadPreferences();
        },5)
    }
    componentWillUnmount() {
        window.removeEventListener('click', this._handleOutsideClick, false);
    }


    loadPreferences() {
        const monthId = preferences.get(preferences.keys.DATEPICKER_MONTH);
        const dateFrom = preferences.get(preferences.keys.DATEPICKER_FROM);
        const dateTo = preferences.get(preferences.keys.DATEPICKER_TO);
        const filterType = preferences.get(preferences.keys.DATEPICKER_FILTER_TYPE);
        if (monthId !== null) {
            this.props.changeMonth(monthId);
        }
        if (dateFrom !== null) {
            this.props.changeFromDate(moment(dateFrom));
        }
        if (dateTo !== null) {
            this.props.changeToDate(moment(dateTo));
        }
        if (filterType !== null) {
            this.props.changeRadio(filterType);
        }
    }
    getRange() {
        const { datepickerFilter, month, dateFrom, dateTo} = this.props;
        if (datepickerFilter === 'month') {
            return {
                start: moment().month(month).startOf('month').format('D. MMMM YYYY'),
                end: moment().month(month).endOf('month').format('D. MMMM YYYY')
            }
        }

        if (datepickerFilter === 'from_to') {
            return {
                start: dateFrom.format('D. MMMM YYYY'),
                end: dateTo.format('D. MMMM YYYY'),
            };
        }
    }

    toggleSelect(show = null) {
        if (show !== null) {
            this.setState(() => ({open: show}));
        }

        if (show === null) {
            this.setState(() => ({open: !this.state.open}));
        }
    }

    _handleSelectedClick() {
        this.toggleSelect();
    }

    _handleOutsideClick(e) {
        if (!ReactDOM.findDOMNode(this).contains(e.target) && this.state.open) {
            this.toggleSelect(false);
        }
    }

    _handleFromDatepickerChange(date) {
        if (this.props.dateFrom.toDate() !== date.toDate()) {
            this.props.changeFromDate(date);
            preferences.store(preferences.keys.DATEPICKER_FROM, date.toDate());
        }
    }

    _handleToDatepickerChange(date) {
        if (this.props.dateTo.toDate() !== date.toDate()) {
            this.props.changeToDate(date);
            preferences.store(preferences.keys.DATEPICKER_TO, date.toDate());
        }
    }

    _handleMonthChange(month) {
        if (this.props.month !== month) {
            this.props.changeMonth(month);
            preferences.store(preferences.keys.DATEPICKER_MONTH, month);
        }
    }

    _handleRadioChange(filterType) {
        if (this.props.datepickerFilter !== filterType) {
            this.props.changeRadio(filterType);
            preferences.store(preferences.keys.DATEPICKER_FILTER_TYPE, filterType);
        }

    }

    render() {
        const dropdownClass = this.state.open ? styles.dropdownActive : styles.dropdown;
        const dropdownButtonClass = this.state.open ? styles.selectedActive : styles.selected;
        const arrowClass = this.state.open ? styles.arrowActive : styles.arrow;
        const range = this.getRange();
        return (
            <div className={styles.container}>
                <div className={dropdownButtonClass} onClick={this._handleSelectedClick}>
                    <div className={styles.date}>{range.start}<span>to</span>{range.end}</div>
                    <div className={arrowClass}>
                        <Arrow up={this.state.open} size={10} invert={this.props.invert}/>
                    </div>
                </div>
                <div className={dropdownClass}>
                    <div className={styles.datepickerRow}>
                        <div>
                            <RadioCheck label={'Filter by Month'} type="radio" id="month" name="datepicker_radio" value="month" checked={this.props.datepickerFilter === 'month'} onChange={this._handleRadioChange}/>
                        </div>
                        <div className={styles.datepickerRowInner}>
                            <span>Month</span>
                            <div className={styles.inputItem}>
                                <Select
                                    options={months}
                                    onChange={this._handleMonthChange}
                                    optionsToShow={12}
                                    small
                                    defaultOption={this.props.month.toString()}
                                />
                            </div>
                        </div>
                    </div>
                    <div className={styles.datepickerRow}>
                        <div>
                            <RadioCheck label={'Filter by Date range'} type="radio" id="from" name="datepicker_radio" value="from_to" onChange={this._handleRadioChange} checked={this.props.datepickerFilter === 'from_to'}/>
                        </div>
                        <div className={styles.datepickerRowInner}>
                            <span>from</span>
                            <div className={styles.inputItem}>
                                <DatePickerWrapper date={this.props.dateFrom} onChange={this._handleFromDatepickerChange}/>
                            </div>
                        </div>
                    </div>
                    <div className={styles.datepickerRow}>
                        <div className={styles.datepickerRowInner}>
                            <span>to</span>
                            <div className={styles.inputItem}>
                                <DatePickerWrapper date={this.props.dateTo} onChange={this._handleToDatepickerChange}/>
                            </div>
                        </div>
                    </div>
                </div>
            </div>
        );
    }
}

DatePickerDropdown.propTypes = {};
DatePickerDropdown.defaultProps = {};


const mapStateToProps = state => {
    const { monthSelect, dateFrom, dateTo, datepickerFilter } = state.postsFilters;
    return {
        month: monthSelect,
        dateFrom,
        dateTo,
        datepickerFilter
    }
};

const mapDispatchToProps = dispatch => {
    return {
        changeMonth: (monthId) => dispatch(changeMonth(monthId)),
        changeFromDate: (date) => dispatch(changeFromDate(date)),
        changeToDate: (date) => dispatch(changeToDate(date)),
        changeRadio: (val) => dispatch(changeRadioDatepicker(val)),
    }
};

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

我想要实现的是从localStorage加载首选项。如果我在setTimeout()中调用this.loadPreferences()并延迟100ms,它确实有效,尽管感觉不对。

我想这个问题源于这样一个事实:我正在更新我映射到该组件的相同道具。什么是实现我的目标更好的方法?

编辑:添加整个来源以避免混淆

1 个答案:

答案 0 :(得分:0)

尝试将click hanlder显式绑定到context this._handleOutsideClick.bind(this)