简体   繁体   English

在componentDidMount()中反应调度动作

[英]React dispatching action in componentDidMount()

I've been trying to dispatch an action in componentDidMount, unfortunately I'm getting: 我一直在尝试在componentDidMount中调度一个动作,不幸的是,我得到了:

Maximum call stack size exceeded 超出最大呼叫堆栈大小

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);

What I'm trying to achieve is to load preferences from localStorage. 我想要实现的是从localStorage加载首选项。 If i wrap the call of this.loadPreferences() in a setTimeout() with a delay of 100ms it does work, though that doesn't feel right. 如果我以100ms的延迟将this.loadPreferences()的调用包装在setTimeout()中,则它确实可以工作,尽管感觉不对。

I guess the issue stems from the fact that I'm updating the same props that I'm mapping to that component. 我猜这个问题源于我正在更新要映射到该组件的相同道具的事实。 What would be a better approach to achieve my goal? 有什么更好的方法可以实现我的目标?

EDIT: added whole source to avoid confusion 编辑:添加整个源,以避免混淆

尝试将点击处理程序显式绑定到上下文this._handleOutsideClick.bind(this)

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM