useRef使用场景
- 获取子组件或 DOM节点的句柄
- 同步渲染不同周期所共享的的数据
- 渲染周期之间共享数据的存储
- 定时器
- 拖拽
- 事件,鼠标移动
- 第三方组件,echarts.js, d3.js
- useRef 不能被函数组件使用
- 函数组件不能100% 代替类组件
useRef可以“跨渲染周期”保存数据,只会创建一次,不会多次创建
createRef会多次创建
state 跨渲染周期,也就是在组件被多次渲染之后依旧不变的属性?
一个组件的state可以在多次渲染之后依旧不变。但是,state的问题在于一旦修改了它就会造成组件的重新渲染
用useRef来跨越渲染周期存储数据,而且对useRef修改也不会引起组件渲染
用 ref对象的current属性来存储定时器的ID,可以在多次渲染之后依旧保存定时器ID,从而能正常清除定时器
ref.current
https://juejin.cn/post/6972850126666596383
class
class App {
it = 0
}
hooks
function App () {
const it = useRef(0)
}
forwardRef
ref & forwardRef
https://blog.csdn.net/qq_34086980/article/details/104791790
import React, { createRef, useImperativeHandle, forwardRef } from "react"
// 父组件
class Parent extends React.Component {
constructor() {
super()
this.ref = createRef()
}
componentDidMount() {
this.ref.current.alert()
}
render() {
return <ForwardFunctionChild ref={this.ref} />
}
}
export default Parent
// 子组件接收 props & ref
function FunctionChild(props, ref) {
useImperativeHandle(ref, () => ({
alert() {
alert("This is function component")
}
}))
return <span>This is function child</span>
}
const ForwardFunctionChild = forwardRef(FunctionChild)
createRef
- 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 } }
<a name="557b9fb0"></a>
## ref函数子组件
1. 函数式组件无法获取ref,重复踩坑
2. ref获取到的是子组件的实例对象,函数式没有this属性,获取为 undefined
1. **利用回调函数**达到赋值操作
2. 函数式子组件,打印 ref一直是 undefined
3. [https://blog.csdn.net/weixin_33971977/article/details/86027673](https://blog.csdn.net/weixin_33971977/article/details/86027673)
<a name="6n12r"></a>
### 函数父组件获取 函数子组件
1. useRef
```jsx
// 父组件
import React, { memo, useRef } from 'react'
import Form from './Form'
function ModalForm (props) {
const fileRef = useRef(null)
const formRef = useRef(null)
function onValidate () {
const form = formRef.current
form.validateFields(err => {
if (err) return // 让出错的代码先返回
console.log('通过验证', err)
})
console.log('ref', formRef)
}
render () {
return (
<Modal visible={visible}>
<Form ref={ ref => formRef.current = ref } />
<div onClick={() => fileRef.current.click() }>
<input type="file" hidden ref={fileRef}/>
</div>
</Modal>
)
}
}
export default memo(ModalForm)
// 子组件
<Form ref={props.ref}>
<Form.Item>
{ getFieldDecorator('name', {})(<Input />) }
</Form.Item>
</Form>
class父组件获取函数子组件
React.createRef()
获取子组件- 通过React.createRef()创建ref,挂载到组件上
- 16.3+ 使用此方法来创建ref,将其赋值给一个变量
- 通过
ref
挂载在dom节点或组件上,该ref的current属性将能拿到dom节点或组件的实例
// 父组件
import React, { PureComponent, createRef } from 'react'
import Form from './Form'
class ModalForm extends PureComponent {
constructor(props){
super(props)
this.formRef = createRef()
}
onValidate = () => {
const form = this.formRef.current
form.validateFields(err => {
if (err) return // 让出错的代码先返回
console.log('通过验证', err)
})
console.log('ref', this.formRef.current)
}
render () {
return (
<Modal visible={visible}>
<Form ref={this.formRef} />
</Modal>
)
}
}
class父组件获取 class子组件
import react,{Component} from 'react'
import Child from './child'
// parent
class Parent extends Component{
onClick = () => {
//调用子组件的方法
this.child && this.child.onClick()
}
render(){
return(
<div>
// 把子组件的this指针挂载成父组件的一个变量
<Child onRef={ ref => this.child = ref}></Child>
<button onClick={ this.onClick }>点击</button>
</div>
)
}
}
export default Parent;
// child
export default class Child extends Component{
constructor(props){
super(props)
// 如果父组件传来该方法 则调用方法将子组件this指针传过去
props.onRef && props.onRef(this)
this.state = {
value: 100
}
}
onClick =()=>{
console.log('父组件调用子组件方法', this.state.value)
}
render() {
return <div><div>
}
}
注意this指向问题
https://blog.csdn.net/fuhegegnw/article/details/110009760
ref错误
- index.js:2177 Warning: Function components cannot be given refs. Attempts to access this ref will fail. Did you mean to use React.forwardRef()?
- Check the render method of
BaseForm
. - 修改,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)
4. Form.create {withRef: true}的话,会把这个ref的实例包裹起来,并使之有效
5. 引出新的问题:
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)
<a name="mtCMr"></a>
### ref不能重复
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)
<a name="rbiMJ"></a>
### forwardRef
forwardRef render functions do not support propTypes or defaultProps. Did you accidentally pass a React component?<br />原因:forwardRef会让 stateless组件的defaultProps和propTypes属性失效<br />正确的写法:
```jsx
import React, {forwardRef} from 'react';
const App = forwardRef((props, ref) => {
return <div></div>;
})
App.displayName = 'App';
App.defaultPorps = {
value: PropTypes.array
}
export default App;
不推荐的写法,控制台会报警告⚠️
function UserFrom(props, parentRef) {}
export default React.forwardRef(UserFrom)
优化的写法,正确额的
function UserFrom(props, parentRef) {}
const UserFromRef = React.forwardRef(UserFrom)
UserFromRef.propTypes = {
onChange: PropTypes.func.isRequired,
}
export default UserFromRef;
forwardRef参考资料:https://stackoverflow.com/questions/59716140/using-forwardref-with-proptypes-and-eslint