React Hooks
前言
在使用函数组件时,当组件的state(使用hook获得的)或者props发生变化时会引起函数组件的重新执行和渲染。
一、有可能造成re-renders问题
App.js
function App() {
const [total, setTotal] = useState(1);
console.log("app rendered");
console.log("total", total);
// debugger
const isOnline = useTest(total);
// console.log(isOnline);
return (
<div className="App">
<button onClick = {()=>{setTotal(total + 1)}}>total</button>
<p>{total}total</p>
{/* {useMemo(()=> {
return <Test />
}, [])} */}
{isOnline ? <Test /> : null}
</div>
);
}
export default App;
useTest.js
import {useState} from "react";
export default function useTest(id) {
const [isOnline, setIsoline] = useState(false);
if (id % 2 === 0) {
setIsoline(!isOnline);
}
console.log("useTest执行了");
return isOnline;
}
初始值若为1,没有问题
当点击按钮,total
变为2,因为return的组件中使用了total
,因此会引起页面的重新渲染。函数组件在重新渲染的过程中会把函数重新执行一遍,这就造成了会再次执行useTest(total)
,这样就会造成死循环。
二、函数组件中父组件如何获取子组件的实例或者DOM元素
使用useImperativeHandle
和forwardRef
。
1.父组件
function App() {
const [total, setTotal] = useState(1);
console.log("app rendered");
console.log("total", total);
const parentRef = useRef();
// debugger
// const isOnline = useTest(1);
// console.log(isOnline);
return (
<div className="App">
<button onClick = {()=>{setTotal(total + 1)}}>total</button>
<p>{total}total</p>
{/* {useMemo(()=> {
return <Test />
}, [])} */}
<Count></Count>
<FancyInput ref={parentRef}></FancyInput>
<button onClick={() => {parentRef.current.focus()}}></button>
</div>
);
}
在父组件中,使用useRef()
创建一个ref
用来保存子组件中的ref
,通过子组件中的ref
属性传进去。
至于为什么要使用useRef()
来保存子组件中的ref
,因为函数组件每次渲染时都会执行函数,若是通常申请的变量则会重新赋值,但是钩子函数就会把变量挂载到组件APP实例上?
2.子组件
import {useRef, useImperativeHandle, forwardRef} from "react";
function FancyInput(props, parentRef) {
const inpuRef = useRef();
useImperativeHandle(parentRef, () => ({
focus: () => {
inpuRef.current.focus();
}
}))
return <input ref={inpuRef}></input>
}
FancyInput = forwardRef(FancyInput);
export default FancyInput;
子组件完成的任务是拿到parentRef
,将input
的ref
的方法挂载到parentRef
上。(理解可能不准确,后续再查资料)。