anobilisgorse
anobilisgorse

Reputation: 906

Auto adjust dependent state and vice versa when changing

So I am using Airbnb's react-dates library for a calendar:

Component

export const Calendar = ({setDates, startDate, endDate, setFocused, focusedInput}) => {
return (
    <div className="input-group">
        <div className="input-group_addon">
            <i className="icon-date-inactive" aria-hidden="true"></i>
        </div>
        <DateRangePicker
            startDate={startDate}
            endDate={endDate}
            focusedInput={focusedInput}
            displayFormat="ddd, D MMM"
            onDatesChange={setDates}
            onFocusChange={setFocused}
        />
    </div>
    )
}

Container

const mapStateToProps = (state) => {
    return {
        startDate: state.model.model.calendar.startDate,
        endDate: state.model.model.calendar.endDate,
        focusedInput: state.model.model.calendar.focusedInput
    }
}

const mapDispatchToProps = (dispatch) => {
    return {
        setDates: (dates) => {
            dispatch(marketplaceSetDates(dates.startDate, dates.endDate));
        },
        setFocused: (focusedInput) => {
            dispatch(marketplaceSetFocused(focusedInput));
        }
    }
}

export const CalendarContainer = connect(mapStateToProps, mapDispatchToProps)(Calendar);

Actions

export function marketplaceSetDates(startDate, endDate) {
    return {
        type: 'MARKETPLACE_MODEL_DATES_CHANGE',
        dates: {
            start: startDate,
            end: endDate
        }
    }
}

export function marketplaceSetFocused(focusedInput) {
    return {
        type: 'MARKETPLACE_MODEL_FOCUS_CHANGE',
        focusedInput: focusedInput
    }
}

Reducer

let initialState = {
    model: {
        calendar: {
            startDate: moment().add(1, 'day'),
            endDate: moment().add(4, 'day'),
            focusedInput: null
        }
    },
}

export const modelReducer = (state = initialState, action) => {
    let newState = {};
    switch(action.type) {
        case 'MARKETPLACE_MODEL_FOCUS_CHANGE':
            newState = Object.assign({}, state);
            newState.model.calendar.focusedInput = action.focusedInput;
            return newState;

        case 'MARKETPLACE_MODEL_DATES_CHANGE':
            newState = Object.assign({}, state);

            newState.model.calendar.startDate = (action.dates.start === null) ?
            state.model.calendar.startDate : action.dates.start;

            newState.model.calendar.endDate = (action.dates.end === null) ?
            state.model.calendar.startDate.add(4, 'day') : action.dates.end;

            return newState;
    }
    return state;
}

The one rule for our calendar is that, whenever the user select a new startDate or endDate, it must automatically adjust to be at least 3 days apart.

For example as you could see, the initial state sets the dates to be tomorrow and 4 days from now.

But with my current code, when I click on the startDate, both startDate and endDate is set to 3 days after, which should only be endDate's value.

I suspect the reason behind this is due to mutating the state in redux is asynchronous, thus the state.model.calendar.startDate.add(4, 'day') goes first before the newState.model.calendar.startDate = ..., am I correct in my assumption?

Otherwise, I would like help for a workaround.

Upvotes: 0

Views: 42

Answers (1)

iamnat
iamnat

Reputation: 4166

TL;DR: Change the add days function to: state.model.calendar.startDate.clone().add(4, 'day')

I believe the issue is with the momentjs add function. From the docs: http://momentjs.com/docs/#/manipulating/add/

Add

... Mutates the original moment by adding time.

This means that when you do state.model.calendar.startDate.add(4, 'day') it actually changes the value of state.model.calendar.startDate.

So ideally, you should copy the moment and then change it using clone()

Upvotes: 2

Related Questions