businessfragmentation

useImperativeHandle

可以在子组件内部暴露给父组件 句柄 父组件可以调用子组件的方法,或者访问子组件的属性,就类似于Vue的 defineExpose

用法

import React ,{useImperativeHandle} from 'react'
useImperativeHandle(ref, ()=>{
    return {
        // 暴露给父组件的方法或属性
    }
}, [deps])

参数

  • ref: 父组件传递的ref对象
  • createHandle: 返回值,返回一个对象,对象的属性就是子组件暴露给父组件的方法或属性
  • deps?: 可选 依赖项,当依赖项发生变化时,会重新调用createHandle函数,类似于 useEffect 的依赖项

执行时机第三个参数

  1. 如果不传入第三个参数,那么 useImperativeHandle 会在组件挂载时执行一次,然后状态更新时,都会执行一次
  2. 如果传入第三个参数,并且是一个空数组,那么 useImperativeHandle 会在组件挂载时执行一次,然后状态更新时,不会执行
  3. 如果传入第三个参数,并且有值,那么 useImperativeHandle 会在组件挂载时执行一次,然后会根据依赖项的变化,决定是否重新执行

实际案例

例如,我们封装了一个表单组件,提供了两个方法:校验和重置。使用 useImperativeHandle 可以将这些方法暴露给父组件,父组件便可以通过 ref 调用子组件的方法。
interface ChildRef {
   name: string
   validate: () => string | true
   reset: () => void
}

const Child = ({ ref }: { ref: React.Ref<ChildRef> }) => {
   const [form, setForm] = useState({
      username: '',
      password: '',
      email: ''
   })
   const validate = () => {
      if (!form.username) {
         return '用户名不能为空'
      }
      if (!form.password) {
         return '密码不能为空'
      }
      if (!form.email) {
         return '邮箱不能为空'
      }
      return true
   }
   const reset = () => {
      setForm({
         username: '',
         password: '',
         email: ''
      })
   }
   useImperativeHandle(ref, () => {
      return {
         name: 'child',
         validate: validate,
         reset: reset
      }
   })
   return <div style={{ marginTop: '20px' }}>
      <h3>我是表单组件</h3>
      <input value={form.username} onChange={(e) => setForm({ ...form, username: e.target.value })} placeholder='请输入用户名' type="text" />
      <input value={form.password} onChange={(e) => setForm({ ...form, password: e.target.value })} placeholder='请输入密码' type="text" />
      <input value={form.email} onChange={(e) => setForm({ ...form, email: e.target.value })} placeholder='请输入邮箱' type="text" />
   </div>
}

function App() {
   const childRef = useRef<ChildRef>(null)
   const showRefInfo = () => {
      console.log(childRef.current)
   }
   const submit = () => {
      const res = childRef.current?.validate()
      console.log(res)
   }
   return (
      <div>
         <h2>我是父组件</h2>
         <button onClick={showRefInfo}>获取子组件信息</button>
         <button onClick={() => submit()}>校验子组件</button>
         <button onClick={() => childRef.current?.reset()}>重置</button>
         <hr />
         <Child ref={childRef}></Child>
      </div>
   );
}

export default App;