React函数式组件值之useRef()和useImperativeHandle()
一、useRef
useRef共有两种用法,获取子组件的实例(只有类组件可用),在函数组件中的一个全局变量,不会因为重复 render 重复申明, 类似于类组件的 this.xxx。
1. useRef 在使用的时候,可以传入默认值来指定默认值,需要使用的时候,访问 ref.current 即可访问到组件实例:
// 使用 ref 子组件必须是类组件 class Children extends PureComponent { render () { const { count } = this.props return ( <div>{ count }</div> ) } } function App () { const [ count, setCount ] = useState(0) const childrenRef = useRef(null) const onClick = useMemo(() => { return () => { console.log(childrenRef.current) setCount((count) => count + 1) } }, []) return ( <div> 点击次数: { count } <Children ref={childrenRef} count={count}></Children> <button onClick={onClick}>点我</button> </div> ) }
2. 有些情况下,我们需要保证函数组件每次 render 之后,某些变量不会被重复申明,比如说 Dom 节点,定时器的 id 等等,在类组件中,我们完全可以通过给类添加一个自定义属性来保留,比如说 this.xxx, 但是函数组件没有 this,自然无法通过这种方法使用,有的朋友说,我可以使用useState 来保留变量的值,但是 useState 会触发组件 render,在这里完全是不需要的,我们就需要使用 useRef 来实现了,具体看下面例子:
function App () { const [ count, setCount ] = useState(0) const timer = useRef(null) let timer2 useEffect(() => { let id = setInterval(() => { setCount(count => count + 1) }, 500) timer.current = id timer2 = id return () => { clearInterval(timer.current) } }, []) const onClickRef = useCallback(() => { clearInterval(timer.current) }, []) const onClick = useCallback(() => { clearInterval(timer2) }, []) return ( <div> 点击次数: { count } <button onClick={onClick}>普通</button> <button onClick={onClickRef}>useRef</button> </div> ) }
二、useImperativeHandle
useImperativeHandle 可以让你在使用 ref 时自定义暴露给父组件的实例值,说简单点就是,子组件可以选择性的暴露给副组件一些方法,这样可以隐藏一些私有方法和属性,官方建议,useImperativeHandle应当与 forwardRef 一起使用,具体如何使用看下面例子
function Child (props, ref) { const child = useRef() const introduce = useCallback (() => { console.log('i can sing, jump, rap, play basketball') }, []) useImperativeHandle(ref, () => ({introduce})); return ( <div ref={child}> { props.count }</div> ) } const ChildChild = forwardRef(Child) function App () { const [ count, setCount ] = useState(0) const childRef = useRef(null) const onClick = useCallback (() => { setCount(count => count + 1) childRef.current.introduce() }, []) return ( <div> 点击次数: { count } <ChildChild ref={childRef} count={count}></ChildChild> <button onClick={onClick}>点我</button> </div> ) }