简体   繁体   English

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

[英]Cannot set useState hook value to opposite boolean

I'm creating a DatePicker and using useState hook to manage it's visibility.我正在创建一个 DatePicker 并使用 useState 钩子来管理它的可见性。 On div click I've added the event listener which changes value, but it didn't work as I expected.在 div 单击时,我添加了更改值的事件侦听器,但它没有按我预期的那样工作。 It works only the first time, so initial value changes to true, but on second and third clicks this value stays to true and DatePicker stays visible on click.它仅在第一次起作用,因此初始值更改为 true,但在第二次和第三次单击时,此值保持为 true,并且 DatePicker 在单击时保持可见。

This is 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 which uses two.其中使用两个 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;

a) It is a good practice to use functional updates to make sure to use correct "current" value when the next state is dependent on the previous (== current) state: a) 当下一个状态依赖于前一个(== 当前)状态时,使用功能更新确保使用正确的“当前”值是一个很好的做法:

setIsOpen(currentIsOpen => !currentIsOpen)

b) It's very hard to reason about the next state when it gets updated by multiple handlers executed for the same event. b) 当下一个状态被为同一事件执行的多个处理程序更新时,很难推断下一个状态。 Following 2 handlers might execute on the same click (the 1st div is "outside"):以下 2 个处理程序可能会在同一次点击中执行(第一个 div 是“外部”):

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

If onOutsideClick executes first, then React re-renders with isOpen=false , and then onClick executes second, it would set isOpen=true as you observe - I don't see how the re-render could happen between, but maybe OutsideClickHandler is doing something nefarious or your code is more complicated than in the question ¯\\_(ツ)_/¯如果onOutsideClick首先执行,然后 React 使用isOpen=false重新渲染,然后onClick执行第二个,它会设置isOpen=true正如你所观察到的 - 我不知道重新渲染是如何发生的,但也许OutsideClickHandler正在做邪恶的东西或者你的代码比问题更复杂¯\\_(ツ)_/¯

To enforce only 1 event handler:仅强制执行 1 个事件处理程序:

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

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

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