React中箭头函数用ref未定义?点击外部切换遇this.node未定义错误
问题1:在React中使用箭头函数时出现ref未定义的问题?
其实箭头函数本身并不会直接导致ref未定义,大概率是你使用ref的场景或者方式不对,我给你梳理两种最常见的情况和解决办法:
情况1:函数组件中用箭头函数定义组件,无法捕获ref
箭头函数形式的函数组件没有自己的实例,如果你直接给这类组件加ref,ref.current会一直是null。这时候得用React.forwardRef来转发ref:
// 错误示例:箭头函数组件无法被ref捕获 const MyComponent = () => <div>Hello</div>; function Parent() { const ref = useRef(null); // 这里ref.current始终是null return <MyComponent ref={ref} />; } // 正确做法:用forwardRef转发ref const MyComponent = React.forwardRef((props, ref) => { return <div ref={ref}>Hello</div>; }); function Parent() { const ref = useRef(null); useEffect(() => { console.log(ref.current); // 能正常拿到div元素 }, []); return <MyComponent ref={ref} />; }
情况2:类组件中过早访问ref
如果是类组件,可能你在组件还没挂载完成的时候就去访问ref了。比如在构造函数里用箭头函数绑定事件,然后在事件里直接访问ref——这时候组件还没渲染,ref还没赋值,自然是undefined。正确的做法是在componentDidMount生命周期或者渲染完成后的回调里访问ref:
class MyComponent extends React.Component { constructor(props) { super(props); this.myRef = React.createRef(); // 错误:此时组件未挂载,this.myRef.current是null this.handleClick = () => console.log(this.myRef.current); } componentDidMount() { // 正确:挂载完成后访问ref console.log(this.myRef.current); } render() { return <div ref={this.myRef}>Hello</div>; } }
问题2:点击元素外部切换内容时,
this.node未定义的错误 这个问题我碰到过好多次,核心原因是**handleOutsideClick函数的this指向不对**!你写的handleOutsideClick是普通函数,当它被window的click事件调用时,this指向的是window对象,而不是你的组件实例,所以this.node自然找不到。
给你两种简单的解决办法:
方法1:在构造函数中绑定this
在组件的构造函数里,把handleOutsideClick的this绑定到组件实例上:
class MyComponent extends React.Component { constructor(props) { super(props); this.state = { visibleContent: false }; this.handleClick = this.handleClick.bind(this); // 绑定handleOutsideClick的this this.handleOutsideClick = this.handleOutsideClick.bind(this); } handleOutsideClick(e) { if (this.node.contains(e.target)) { return; } this.handleClick(); } handleClick() { this.setState(prevState => ({ visibleContent: !prevState.visibleContent })); } componentDidMount() { // 挂载时添加全局点击监听 window.addEventListener('click', this.handleOutsideClick); } componentWillUnmount() { // 卸载时移除监听,避免内存泄漏 window.removeEventListener('click', this.handleOutsideClick); } render() { return ( <div ref={node => { this.node = node; }}> <button onClick={this.handleClick}>Button handler</button> {this.state.visibleContent && <div>Toggle content</div>} </div> ); } }
方法2:把handleOutsideClick改成箭头函数
箭头函数会自动继承外层作用域的this(也就是组件实例),这样就能直接访问this.node了:
class MyComponent extends React.Component { constructor(props) { super(props); this.state = { visibleContent: false }; this.handleClick = this.handleClick.bind(this); } // 改成箭头函数,自动绑定组件实例的this handleOutsideClick = (e) => { if (this.node.contains(e.target)) { return; } this.handleClick(); } handleClick() { this.setState(prevState => ({ visibleContent: !prevState.visibleContent })); } componentDidMount() { window.addEventListener('click', this.handleOutsideClick); } componentWillUnmount() { window.removeEventListener('click', this.handleOutsideClick); } render() { return ( <div ref={node => { this.node = node; }}> <button onClick={this.handleClick}>Button handler</button> {this.state.visibleContent && <div>Toggle content</div>} </div> ); } }
另外还要提醒你:记得在组件卸载时移除全局的click事件监听,不然会造成内存泄漏哦!
内容的提问来源于stack exchange,提问作者Sharon Chai
相关产品推荐
相关产品推荐

