0

I need to look at the sate of a prop inside of an eventhandler to decide on what should happen. But due to stale prop this dont work. I have solved this using useRef, but this will create new eventhandlers and cleaning them up dont work because the eventhandler method is a anonymous function. I dont know how to pass props to it and not create a anonymous function.

I have looked at these stackoverflow anwsers:

https://stackoverflow.com/a/62666824/3727466

https://stackoverflow.com/a/64770671/3727466

Do anyone have an idea to improve this?

Edit: To awnser my own question, I fixed this by creating a method inside of the useEffect. This creates a method with a reference and the eventhandler can remove itself when the props changes before adding a new listener. But I more than welcome other suggestions or solutions on how to handle this situation. Where you need to check some changing prop inside of an eventhandler.

EditTwo: Looks like the useEffectEvent will solve edgeCases like this in the future: https://react.dev/reference/react/experimental_useEffectEvent

https://react.dev/learn/separating-events-from-effects#declaring-an-effect-event

const SearchInputCollapsible = ({
    handleSearchChange,
    searchText,
    alwaysOpen,
}) => {
    const [isOpen, setIsOpen] = useState(alwaysOpen || false);
    const ref = useRef();
    const inputRef = useRef(null);
    const getRef = (r) => {
        if (inputRef.current !== r.current) inputRef.current = r.current;
    };
    const shouldClose = (event, text, open) => {
        console.log(" ~ shouldClose")
        if (text.length > 0) return;
        if (event.type === "focusout") {
            handleSearchChange("");
            setIsOpen(false);
            return;
        }
        const rect = ref.current?.getBoundingClientRect();
        const isOutsideOfInput =
            rect &&
            (event.clientY < rect.top ||
                event.clientY > rect.bottom ||
                event.clientX < rect.left ||
                event.clientX > rect.right);
        if (open && !isOutsideOfInput) return;
        handleSearchChange("");
        setIsOpen(false);
    };


useEffect(() => {
    if (alwaysOpen) return;
    const handleClose = (event) => {
        shouldClose(event, searchText, isOpen);
    };

    if (isOpen) {
        inputRef?.current?.focus();
        document.addEventListener("mousedown", handleClose);
        ref.current?.addEventListener("focusout", handleClose);
    }

    return () => {
        document.removeEventListener("mousedown", handleClose);
        ref.current?.removeEventListener("focusout", handleClose);
    };
}, [isOpen, searchText]);

    const clearAndClose = () => {
        if (!alwaysOpen) setIsOpen(false);
        handleSearchChange("");
    };
    const handleOpenCloseClick = () => {
        if (alwaysOpen) return;
        setIsOpen(!isOpen);
        handleSearchChange("");
    };
    return (
        <Wrapper {...{ isOpen, alwaysOpen, showClearButton: searchText }}>
            <Icon {...{ onClick: handleOpenCloseClick }} />
            <CollapseWrapper {...{ isOpen }}>
                <InnerCollapseWrapper>
                    <Input
                        {...{
                            value: searchText,
                            handleEvent: (e) => handleSearchChange(e.target.value),
                            getRef,
                        }}
                    />
                </InnerCollapseWrapper>
            </CollapseWrapper>
            {isOpen && <CloseButton {...{ onClick: clearAndClose }} />}
        </Wrapper>
    );
};
J4v4Scr1pt
  • 289
  • 5
  • 16

0 Answers0