foreword
In a parent component, we want to get the node element or child component instance in it, so as to directly call the method on it. Class class components and function components are two different ways of writing.
1. Use ref in the Class component
In React's Class component, we createRef
create ref by
class Parent extends React.Component {
constructor(props) {
super(props)
this.inputRef = React.createRef();
}
componentDidMount() {
console.log(this.inputRef)
this.inputRef.current.focus();
}
render() {
return <input ref={
this.inputRef} />
}
}
In the above code example, we used to createRef
create one ref
, and hung it on the native DOM element input. At this time, we can ref.current
get this DOM element through and call the above method.
If ref is mounted on a Class component, ref.current gets the instance of the Class component, and can also access the methods of the Class component.
Take the above page as an example, the following [Drawing Plan] is a sub-component, when we mount ref
to the sub-component, the corresponding parent component can this.ref.current
call the method on the sub-component, such as the drop-down box to expand Callbacks, callbacks for drop-down box value changes, etc.
2. Functional components
ref 回调函数
After the component is mounted, the component instance will be passed to the function. The function component is different from the Class component, and the function component has no instance . So under normal circumstances, ref cannot be mounted on function components.
However, function components can create refs. Unlike class components, function components use useRef
.
import React, {
memo, useEffect, useRef, useState } from 'react';
const Parent = () => {
const inputRef = useRef(null)
input childRef = useRef(null)
return (
<>
<input ref={
inputRef} onChange={
onChange} />
<Child ref={
childRef} handSave={
handSave} />
</>
)
}
export default memo(Parent)
Note : This way of writing is grammatically correct, but if you want to ref.current
get an instance of a DOM element or subcomponent in a function component, you can't get it, and you need another way of writing.
3. The difference between createRef and useRef
- createRef can only be used in Class class components, and useRef can only be used in functional components.
- createRef returns a new ref every render, useRef returns the same ref every time.
- If the ref created by createRef is used in a functional component, its value will be continuously initialized as the functional component is re-executed .
4. Partial conclusions
In summary, the following points can be summarized:
- Native DOM element: the current of ref points to this node
- class class component: the current of ref points to the component instance
- Functional component: the current of ref points to null, because the functional component has no instance.
So for functional components, is there really no way for us to get and call the methods on the subcomponents?
Answer: How is it possible, we can forwardRef
solve this problem by using .
5. forwardRef
forwardRef (passing by reference) is a technique for automatically passing a reference ref from a component to a child component.
In one sentence: React uses forwardRef to complete the transparent transmission of ref, so that functional components can normally obtain the methods on subcomponents.
5.1 Code writing
import React, {
memo, forwardRef, useRef, useImperativeHandle } from 'react';
const App = () => {
const childRef = useRef(null)
const getFocus = () => {
// 调用子组件的方法
childRef.current.inputFocus()
// 也可以调用暴露出来的其他值
childRef.current.setData(20)
}
return (
<>
<Child ref={
childRef}/>
<button onClick ={
getFocus}>点击获取焦点<button/>
</>
)
}
// 子组件
const Child = forwardRef((props, ref) => {
const inputRef = useRef(null)
const [data, setData] = useState('10')
// 使输入框获取焦点的方法
const inputFocus = () => {
inputRef.current.ocus()
}
// 输入框内容改变回调
const changeValue = () => {
console.log('哈哈哈')
}
// 将该方法暴露给父组件
useImperativeHandle(ref, () => ({
inputFocus,
changeValue,
data,
setData
}))
return <input ref={
inputRef} onChange={
changeValue}>
});
5.2 forwardRef parsing
forwardRef
You can directly wrap a functional component , and the wrapped functional component will get the ref assigned to itself (as the second parameter). If you directly assign ref to a functional component that is not wrapped by forwardRef, React will report an error in the console.
forwardRef
A React component will be created, which can forward the ref attribute it accepts to another component under its component tree, that is, transparent transmission.
Note: the parameter of forWardRef must be function
6. useImperativeHandle parsing
When used directly forwardRef
, we cannot control the value to be exposed to the parent component, so we use to useImperativeHandle
control what to expose to the parent component.
Note: useImperativeHandle should be used together with forwardRef.
The calling method is shown in the above code example.
6.1 Parameter passing
- The first parameter: ref : Receive the ref passed from forwardRef.
- The second parameter: createHandle : Handling function, the return value is exposed as a ref object to the parent component.
- The third parameter: deps : dependency deps, dependency changes form a new ref object, can not be passed
useImperativeHandle provides us with an instance-like thing that helps us mount the content of the returned object to the ref.current of the attachment through the second parameter of useImperativeHandle
7. Summary
The steps when the parent and child components use the hooks are as follows:
- The parent component uses to
useRef
create a ref object and assign this ref object to the ref property of the child component. - The child component wraps
forwardRef
itself with a , allowing the ref to be used as a function component itself. Then useuseImperativeHandle
the hook function to return some state or method in the second function parameter of the hook function, and the returned state or method can be accessed by the parent component. - The parent component uses the property of the created ref object
current
to obtain the state or method exposed by the child component.
8. Special attention points:
forwardRef
When using and in functional components useImperativeHandle
, there is a special point that needs attention: the method of passing parameters in subcomponents . As follows:
// 可以使用这样的写法
<Child ref={
childRef} list={
drawingPlanList} editable={
!isDisabled}/>
// 但不用采用这样的写法:这样的 写法有问题,具体原因待排查
const childProps = {
ref: {
childRef},
list: {
drawingPlanList},
editable: {
!isDisabled}
}
< Child {
...childProps}/>