无法将useState挂钩值设置为相反的布尔值

时间:2019-07-27 11:38:10

标签: reactjs

我正在创建一个DatePicker并使用useState挂钩来管理它的可见性。在div上单击,我添加了更改值的事件侦听器,但它没有按我预期的那样工作。它只能在第一次使用,因此初始值更改为true,但是在第二次和第三次单击时,此值保持为true,而DatePicker在单击时保持可见。

这是DatePicker

import React, { useState } from 'react';
import OutsideClickHandler from 'react-outside-click-handler';
import { renderInfo, getWeeksForMonth } from './utils';
import {
    renderMonthAndYear,
    handleBack,
    handleNext,
    weekdays,
} from '../../utils';

const DatePicker = ({
    isOpen,
    setIsOpen,
    selected,
    setSelected,
    dayClick,
    dayClass,
}) => {
    const startDay = new Date().setHours(0, 0, 0, 0);

    const [current, setCurrent] = useState(new Date(startDay));

    const weeks = getWeeksForMonth(current.getMonth(), current.getFullYear());

    function handleClick(date) {
        if (date > startDay) {
            setSelected(date);
            setCurrent(date);
            setIsOpen(false);

            if (dayClick) {
                dayClick(date);
            }
        }
    }

    return (
        <div className="DatePicker-container">
            <div
                tabIndex="0"
                role="button"
                className="DatePicker-info"
                onKeyPress={e => {
                    if (e.which === 13) {
                        setIsOpen(!isOpen);
                    }
                }}
                onClick={e => {
                    setIsOpen(!isOpen);
                }}
            >
                {renderInfo(selected)}
            </div>
            {isOpen && (
                <OutsideClickHandler onOutsideClick={() => setIsOpen(false)}>
                    <div className="DatePicker">
                        <div className="DatePicker__header">
                            <span
                                role="button"
                                onClick={() => handleBack(current, setCurrent)}
                                className="triangle triangle--left"
                            />

                            <span className="DatePicker__title">
                                {renderMonthAndYear(current)}
                            </span>

                            <span
                                role="button"
                                onClick={() => handleNext(current, setCurrent)}
                                className="triangle triangle--right"
                            />
                        </div>

                        <div className="DatePicker__weekdays">
                            {weekdays.map(weekday => (
                                <div
                                    key={weekday}
                                    className="DatePicker__weekday"
                                >
                                    {weekday}
                                </div>
                            ))}
                        </div>
                        {weeks.map((week, index) => (
                            <div
                                role="row"
                                key={index}
                                className="DatePicker__week"
                            >
                                {week.map((date, index) =>
                                    date ? (
                                        <div
                                            role="cell"
                                            key={index}
                                            onClick={() => handleClick(date)}
                                            className={dayClass(date)}
                                        >
                                            {date.getDate()}
                                        </div>
                                    ) : (
                                        <div
                                            key={index}
                                            className="DatePicker__day--empty"
                                        />
                                    ),
                                )}
                            </div>
                        ))}
                    </div>
                </OutsideClickHandler>
            )}
        </div>
    );
};

export default DatePicker;

DateRangePicker,使用两个。

import React, { useState } from 'react';
import DatePicker from './DatePicker';
import DatePickerContext from './DatePickerContext';
import './DatePicker.scss';

const DateRangePicker = () => {
    const startDay = new Date().setHours(0, 0, 0, 0);

    const [isOpen, setIsOpen] = useState(false);
    const [isSecondOpen, setIsSecondOpen] = useState(false);
    const [selected, setSelected] = useState(new Date(startDay));
    const [secondSelected, setSecondSelected] = useState(new Date(startDay));

    function dayClass(date) {
        if (
            selected.getTime() === date.getTime() ||
            (date >= selected && date <= secondSelected)
        ) {
            return 'DatePicker__day DatePicker__day--selected';
        }
        if (date < startDay || date < selected) {
            return 'DatePicker__day DatePicker__day--disabled';
        }
        return 'DatePicker__day';
    }

    function dayClick(date) {
        setSecondSelected(date);
        setIsSecondOpen(true);
    }

    return (
        <DatePickerContext.Provider>
            <div className="DatePicker-wrapper">
                <DatePicker
                    key={1}
                    isOpen={isOpen}
                    setIsOpen={setIsOpen}
                    selected={selected}
                    setSelected={setSelected}
                    dayClick={dayClick}
                    dayClass={dayClass}
                />
                <DatePicker
                    key={2}
                    isOpen={isSecondOpen}
                    setIsOpen={setIsSecondOpen}
                    selected={secondSelected}
                    setSelected={setSecondSelected}
                    dayClass={dayClass}
                />
            </div>
        </DatePickerContext.Provider>
    );
};

export default DateRangePicker;

1 个答案:

答案 0 :(得分:0)

a)好的做法是,当下一个状态取决于上一个(==当前)状态时,使用functional updates确保使用正确的“当前”值:

setIsOpen(currentIsOpen => !currentIsOpen)

b)当下一个状态被针对同一事件执行的多个处理程序更新时,很难推断下一个状态。以下2个处理程序可能会在同一点击上执行(第一个div是“外部”):

<div ... onClick={e => setIsOpen(!isOpen)}>
<OutsideClickHandler onOutsideClick={() => setIsOpen(false)}>

如果先执行onOutsideClick,然后React用isOpen=false重新渲染,然后再执行onClick,那么您将观察到的是isOpen=true设置-我不会了解如何在两次渲染之间进行重新渲染,但是OutsideClickHandler可能在做恶作剧,或者您的代码比问题\ _(ツ)_ /¯

更复杂

仅实施1个事件处理程序:

<OutsideClickHandler onOutsideClick={(e) => {
  e.stopPropagation();
  setIsOpen(false);
}}>