Search code examples
reactjsreact-hooksstatetransition

How to make an element correctly reset its state and pass props while focusing on the input


I try to add focus on the input field when the element is mounted. I use this code. When I press button on Header the search bar shows up, I add to it class open because
want my search to move with a help of transition. Search bar starts moving and get to its place and the focus is applied. it's fine. Then I click "close" button to hide the search bar and it smoothly return to its place(backwards, to become hidden). It's all right till now. Because after that, the search bar shows up again and doesn't hide even though I pressed the close button! I expect that

onClick={() => { props.setVisibleSearchBar(false) }}

will hide my search. By the way, the style for "open" also doesn't applied when the search appears unexpectevely after the close btn is clicked(though the search is visible).

Header.jsx

 const Header = () => {
 const [visibleSearchBar, setVisibleSearchBar] = useState(false);
 return (
 <button onClick={() => setVisibleSearchBar(true)}>Show search bar</button>
                                                       
 <Search visibleSearchBar={visibleSearchBar}
                    setVisibleSearchBar={setVisibleSearchBar} />
  );
}

Search.jsx

 const Header = (props) => {
 const [disabled, setDisabled] = useState(true);
  let slideRef = useRef('');
  let inputRef = useRef('');

    useEffect(() => {
    const callback = () => {
        setDisabled(false);
        inputRef.current.focus();
    }
    slideRef.current.addEventListener('transitionend', callback);
    return () => slideRef.current.removeEventListener('transitionend', callback)
}, []);

 return (
    <div
        className={classNames('header-inner__main-search-slide', { open: props.visibleSearchBar })}
        ref={slideRef}>

        <input ref={inputRef}
            disabled={disabled}
            className='header-inner__main-search-slide-field' />
       
        <button
            onClick={() => { props.setVisibleSearchBar(false) }}
            className='header-inner__main-search-slide-close'> CLOSE 
        </button>
    </div >
)

Solution

  • Played with your sanbox, here it is working fine - https://codesandbox.io/s/ecstatic-buck-d2rgx

    So you problem was here:

        const callback = () => {
            setDisabled(false);
            inputRef.current.focus();
        }
    

    You are setting a focus, whenever some transition finishes. But it is incorrect, you have to focus your input only when "open" transition finishes. It is important, because browser automatically scrolls to the element in focus, that's what was happening in this case, you was hiding input, but then focusing it, so browser "scrolled" it back.

    Here is a fixed version of it:

      if (slideRef.current.classList.contains("open")) {
        inputRef.current.focus();
      }
    

    Why am i not using props here - because, your useEffect will be executed only on first render and if i would use props here, i would use very first props passed to the component (because of closure). If you don't like to have to check classList, other option would be to use state here (move 'open' flag to state)