React中的ref

React中ref还是很常用的,它使父组件具备操作子组件/原生DOM的能力,比如控制一个modal组件弹窗显示等等。但使用上有时会遇到一个不注意的坑,这里梳理下。

  1. ref挂载元素成功后,并不会触发组件重新渲染

    有时会将useEffect依赖中增加ref或者ref.current,但实际上当ref中引用的组件被挂载后,ref.current虽然已经保存了实例引用,但组件并不会触发重新渲染,更别说effect重新执行了,因此遇到此类需求需要修改方案来解决,方法如下

  2. ref的使用,可以直接ref对象赋值,也可以callback,两种方式本身作用是一致的,唯一区别是如果想在挂载时执行一些动作,可以使用callback,举个🌰

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    function App() {
    const pElRef = useRef<any>();
    const [refresh, setRefresh] = useState(0);
    useEffect(() => {
    console.log('render?');
    }, [refresh]);

    const onAttached = useCallback((ref) => {
    if (refresh === 0) {
    setRefresh(refresh + 1);
    }
    pElRef.current = ref;
    }, [refresh]);
    return (
    <div className="App">
    <header className="App-header">
    <img src={logo} className="App-logo" alt="logo"/>
    <p ref={onAttached}>
    Edit <code>src/App.tsx</code> and save to reload.
    </p>
    <a
    className="App-link"
    href="https://reactjs.org"
    target="_blank"
    rel="noopener noreferrer"
    >
    Learn React
    </a>
    </header>
    </div>
    );
    }

​ 如上,增加state参refresh,在挂载成功时修改从而做到重新渲染。

HOC下的ref传递

使用组件时有几个参数是特殊的,比如key,又或者是ref。它们并非属于props,react下会进行特殊处理,比如在HOC下,就需要注意。

举个🌰,如下代码中,App组件中希望可以利用SayRef来操作User组件,但是User组件并不直接提供给App消费,而是HOC进行了下包裹。这里props在传给User时并不会将ref传递,ref引用将指向的是SayHoc,因此这里点击不会work。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
export function SayHoc({ boss }: { boss: string }) {
return (props: any) => {
return <h1>
from:{boss} say hello:
<User {...props}/>
</h1>;
};
}


const User = React.forwardRef((props: { name: string }, ref) => {
useImperativeHandle(ref, () => {
return ({
say: () => alert(props.name),
});
});
return <>{props.name}</>;
});


const Say = SayHoc({ boss: 'L' });

function App() {
const sayRef = useRef<any>();
return (
<div className="App">
<header className="App-header">
<img src={logo} className="App-logo" alt="logo"/>
<Say name={'2121'} ref={sayRef}/>
<button onClick={() => {
sayRef.current?.say();
}}>
say alert
</button>
</header>
</div>
);
}

export default App;

解决办法如下,利用React.forwardRef。

1
2
3
4
5
6
7
8
export function SayHoc({ boss }: { boss: string }) {
return React.forwardRef((props: any, ref) => {
return <h1>
from:{boss} say hello:
<User ref={ref} {...props}/>
</h1>;
});
}

相关文档