ref在事件处理程序中没有值

时间:2019-05-25 15:24:50

标签: reactjs ref forward-reference

目标功能:
用户单击按钮时,将显示一个列表。当他在列表外部单击时,它将关闭并且按钮应获得焦点。 (遵循辅助功能准则)

我尝试过的事情:

  const hideList = () => {
    // This closes the list
    setListHidden(true);
    // This takes a ref, which is forwarded to <Button/>, and focuses it
    button.current.focus();
  }

  <Button
    ref={button}
  />

问题:
当我检查hideList函数的范围时,发现ref在click事件处理程序中的每个位置都获得了对按钮的正确引用,它是{current: null}
控制台输出:Cannot read property 'focus' of null

示例
https://codepen.io/moaaz_bs/pen/zQjoLK
-单击按钮,然后在外部单击并查看控制台。

2 个答案:

答案 0 :(得分:3)

由于您已经在应用程序中使用了挂钩,因此您唯一需要做的更改就是使用useRef而不是createRef来生成对列表的引用。

const Button = React.forwardRef((props, ref) => {
  return (
    <button 
      onClick={props.toggleList} 
      ref={ref}
    >
      button
    </button>
  );
})

const List = (props) => {
  const list = React.useRef();

  handleClick = (e) => {
    const clickIsOutsideList = !list.current.contains(e.target);
    console.log(list, clickIsOutsideList);
    if (clickIsOutsideList) {
      props.hideList();
    }
  }

  React.useEffect(function addClickHandler() {
    document.addEventListener('click', handleClick);
  }, []);

  return (
    <ul ref={list}>
      <li>item</li>
      <li>item</li>
      <li>item</li>
    </ul>
  );
}

const App = () => {
  const [ListHidden, setListHidden] = React.useState(true);

  const button = React.useRef();

  const toggleList = () => {
    setListHidden(!ListHidden);
  }

  const hideList = () => {
    setListHidden(true);
    button.current.focus();
  }

  return (
    <div className="App">
      <Button 
        toggleList={toggleList} 
        ref={button}
      />
      {
        !ListHidden &&
        <List hideList={hideList} />
      }
    </div>
  );
}

ReactDOM.render(<App />, document.getElementById('root'));

Working demo

之所以需要它,是因为如果您使用React.createRef,则在每个功能组件的渲染中都会生成一个新的引用,而useRef的实现是在以下情况下生成一个引用:它是第一次调用,并在以后重新渲染时随时返回相同的引用。

  

PS 。根据经验法则,您可以说useRef应在您使用时   想要在功能组件中包含引用,而createRef   应该在类组件中使用。

答案 1 :(得分:1)

创建参考

this.button = React.createRef();

将引用添加到您的DOM元素

ref={this.button}

根据要求使用参考

this.button.current.focus();

使用转发引用完成代码

const Button = React.forwardRef((props, ref) => {
    return (
        <button
            onClick={props.toggleList}
            ref={ref}
        >
            button
        </button>
    );
})

const List = (props) => {
    const list = React.createRef();

    handleClick = (e) => {
        const clickIsOutsideList = !list.current.contains(e.target);
        if (clickIsOutsideList) {
            props.hideList();
        }
    }

    React.useEffect(function addClickHandler() {
        document.addEventListener('click', handleClick);
        return function clearClickHandler() {
            document.removeEventListener('click', handleClick);
        }
    }, []);

    return (
        <ul ref={list}>
            <li>item</li>
            <li>item</li>
            <li>item</li>
        </ul>
    );
}

const button = React.createRef();

const App = () => {
    const [ListHidden, setListHidden] = React.useState(true);
    const toggleList = () => {
        setListHidden(!ListHidden);
    }

    const hideList = () => {
        setListHidden(true);
        console.log(button)
        button.current.focus();
    }

    return (
        <div className="App">
            <Button
                toggleList={toggleList}
                ref={button}
            />
            {
                !ListHidden &&
                <List hideList={hideList} />
            }
        </div>
    );
}

ReactDOM.render(<App />, document.getElementById('root'));