useRef使用场景

  1. 获取子组件或 DOM节点的句柄
  2. 同步渲染不同周期所共享的的数据
    • 渲染周期之间共享数据的存储
    • 定时器
    • 拖拽
    • 事件,鼠标移动
    • 第三方组件,echarts.js, d3.js
  3. useRef 不能被函数组件使用
    1. 函数组件不能100% 代替类组件

useRef可以“跨渲染周期”保存数据,只会创建一次,不会多次创建
createRef会多次创建

state 跨渲染周期,也就是在组件被多次渲染之后依旧不变的属性?
一个组件的state可以在多次渲染之后依旧不变。但是,state的问题在于一旦修改了它就会造成组件的重新渲染

用useRef来跨越渲染周期存储数据,而且对useRef修改也不会引起组件渲染
用 ref对象的current属性来存储定时器的ID,可以在多次渲染之后依旧保存定时器ID,从而能正常清除定时器


ref.current

https://juejin.cn/post/6972850126666596383

class

  1. class App {
  2. it = 0
  3. }

hooks

  1. function App () {
  2. const it = useRef(0)
  3. }

forwardRef

ref & forwardRef
https://blog.csdn.net/qq_34086980/article/details/104791790

  1. import React, { createRef, useImperativeHandle, forwardRef } from "react"
  2. // 父组件
  3. class Parent extends React.Component {
  4. constructor() {
  5. super()
  6. this.ref = createRef()
  7. }
  8. componentDidMount() {
  9. this.ref.current.alert()
  10. }
  11. render() {
  12. return <ForwardFunctionChild ref={this.ref} />
  13. }
  14. }
  15. export default Parent
  16. // 子组件接收 props & ref
  17. function FunctionChild(props, ref) {
  18. useImperativeHandle(ref, () => ({
  19. alert() {
  20. alert("This is function component")
  21. }
  22. }))
  23. return <span>This is function child</span>
  24. }
  25. const ForwardFunctionChild = forwardRef(FunctionChild)

createRef

  1. class组件 React.createRef() ```jsx import React, { createRef } from “react”

// 父组件 class Parent extends React.Component { constructor() { super() this.ref = createRef() }

componentDidMount() { // 调用子组件的方法 this.ref.current.alert() }

render() { return } }

export default Parent

// 子组件 class ClassChild extends React.Component { alert() { alert(“this is class child”) } render() { return This is class child } }

  1. <a name="557b9fb0"></a>
  2. ## ref函数子组件
  3. 1. 函数式组件无法获取ref,重复踩坑
  4. 2. ref获取到的是子组件的实例对象,函数式没有this属性,获取为 undefined
  5. 1. **利用回调函数**达到赋值操作
  6. 2. 函数式子组件,打印 ref一直是 undefined
  7. 3. [https://blog.csdn.net/weixin_33971977/article/details/86027673](https://blog.csdn.net/weixin_33971977/article/details/86027673)
  8. <a name="6n12r"></a>
  9. ### 函数父组件获取 函数子组件
  10. 1. useRef
  11. ```jsx
  12. // 父组件
  13. import React, { memo, useRef } from 'react'
  14. import Form from './Form'
  15. function ModalForm (props) {
  16. const fileRef = useRef(null)
  17. const formRef = useRef(null)
  18. function onValidate () {
  19. const form = formRef.current
  20. form.validateFields(err => {
  21. if (err) return // 让出错的代码先返回
  22. console.log('通过验证', err)
  23. })
  24. console.log('ref', formRef)
  25. }
  26. render () {
  27. return (
  28. <Modal visible={visible}>
  29. <Form ref={ ref => formRef.current = ref } />
  30. <div onClick={() => fileRef.current.click() }>
  31. <input type="file" hidden ref={fileRef}/>
  32. </div>
  33. </Modal>
  34. )
  35. }
  36. }
  37. export default memo(ModalForm)
  38. // 子组件
  39. <Form ref={props.ref}>
  40. <Form.Item>
  41. { getFieldDecorator('name', {})(<Input />) }
  42. </Form.Item>
  43. </Form>

class父组件获取函数子组件

  1. React.createRef()获取子组件
    1. 通过React.createRef()创建ref,挂载到组件上
  2. 16.3+ 使用此方法来创建ref,将其赋值给一个变量
  3. 通过ref挂载在dom节点或组件上,该ref的current属性将能拿到dom节点或组件的实例
  1. // 父组件
  2. import React, { PureComponent, createRef } from 'react'
  3. import Form from './Form'
  4. class ModalForm extends PureComponent {
  5. constructor(props){
  6. super(props)
  7. this.formRef = createRef()
  8. }
  9. onValidate = () => {
  10. const form = this.formRef.current
  11. form.validateFields(err => {
  12. if (err) return // 让出错的代码先返回
  13. console.log('通过验证', err)
  14. })
  15. console.log('ref', this.formRef.current)
  16. }
  17. render () {
  18. return (
  19. <Modal visible={visible}>
  20. <Form ref={this.formRef} />
  21. </Modal>
  22. )
  23. }
  24. }

class父组件获取 class子组件

  1. import react,{Component} from 'react'
  2. import Child from './child'
  3. // parent
  4. class Parent extends Component{
  5. onClick = () => {
  6. //调用子组件的方法
  7. this.child && this.child.onClick()
  8. }
  9. render(){
  10. return(
  11. <div>
  12. // 把子组件的this指针挂载成父组件的一个变量
  13. <Child onRef={ ref => this.child = ref}></Child>
  14. <button onClick={ this.onClick }>点击</button>
  15. </div>
  16. )
  17. }
  18. }
  19. export default Parent;
  20. // child
  21. export default class Child extends Component{
  22. constructor(props){
  23. super(props)
  24. // 如果父组件传来该方法 则调用方法将子组件this指针传过去
  25. props.onRef && props.onRef(this)
  26. this.state = {
  27. value: 100
  28. }
  29. }
  30. onClick =()=>{
  31. console.log('父组件调用子组件方法', this.state.value)
  32. }
  33. render() {
  34. return <div><div>
  35. }
  36. }

注意this指向问题
https://blog.csdn.net/fuhegegnw/article/details/110009760

ref错误

  1. index.js:2177 Warning: Function components cannot be given refs. Attempts to access this ref will fail. Did you mean to use React.forwardRef()?
  2. Check the render method of BaseForm.
  3. 修改,connect,withRouter,Form.create()新增 {withRef: true} ```jsx export default Form.create({withRef:true})(BaseForm)

export default connect( stateToProps, null, null, { forwardRef: true } )(BaseForm)

export default withTranslation(‘translation’, { withRef: true })(BaseForm)

  1. 4. Form.create {withRef: true}的话,会把这个ref的实例包裹起来,并使之有效
  2. 5. 引出新的问题:
  3. 1. index.js:2177 Warning: `withRef` is deprecated, please use `wrappedComponentRef` instead. See: [https://github.com/react-component/form#note-use-wrappedcomponentref-instead-of-withref-after-rc-form140](https://github.com/react-component/form#note-use-wrappedcomponentref-instead-of-withref-after-rc-form140)
  4. <a name="mtCMr"></a>
  5. ### ref不能重复
  6. 2个组件的 ref不能重复,重复会导致后面的覆盖前面的<br />![image.png](https://cdn.nlark.com/yuque/0/2021/png/112859/1635327791742-171605a0-c651-4404-937a-b5af80bc20ab.png#clientId=u8013eb7e-ed71-4&from=paste&height=217&id=ud956b136&originHeight=650&originWidth=886&originalType=binary&ratio=1&rotation=0&showTitle=false&size=237782&status=done&style=none&taskId=u58b74456-a4e8-4781-ac7d-586cab6c0cb&title=&width=295.3333333333333)
  7. <a name="rbiMJ"></a>
  8. ### forwardRef
  9. forwardRef render functions do not support propTypes or defaultProps. Did you accidentally pass a React component?<br />原因:forwardRef会让 stateless组件的defaultPropspropTypes属性失效<br />正确的写法:
  10. ```jsx
  11. import React, {forwardRef} from 'react';
  12. const App = forwardRef((props, ref) => {
  13. return <div></div>;
  14. })
  15. App.displayName = 'App';
  16. App.defaultPorps = {
  17. value: PropTypes.array
  18. }
  19. export default App;

不推荐的写法,控制台会报警告⚠️

  1. function UserFrom(props, parentRef) {}
  2. export default React.forwardRef(UserFrom)

优化的写法,正确额的

  1. function UserFrom(props, parentRef) {}
  2. const UserFromRef = React.forwardRef(UserFrom)
  3. UserFromRef.propTypes = {
  4. onChange: PropTypes.func.isRequired,
  5. }
  6. export default UserFromRef;

forwardRef参考资料:https://stackoverflow.com/questions/59716140/using-forwardref-with-proptypes-and-eslint