这里承接上一片博客来总结其他的几个hooksapi
目录
- useRef
- forwardRef
- useImperativeHandle
- 自定义hook
useRef
假设,你需要一个值,在组件不断render时保持不变
比如我要记录一个组件渲染了多少次,如果要使用全局变量来记录这个次数,可能会有命名冲突的问题
使用ref可以解决这个问题 ,使用useEffect+useRef 可以记录组件渲染的次数
这个useRef的值和组件一一对应
function App(){
const count=useRef(0)
....
useEffect(()=>{
count.current+=1
})
其他代码
}
初始化:const count=useRef(0)
读取:count.current
- 为什么要用current来读取值呢
为了保证两次取到的是同一个值,其实存的都是一个对象 useRef({current:0})
current表示同一个对象里面的内容.
forwardRef
场景,我希望爸爸组件能够通过ref得到子组件中的button
先自己乱写一波
function App(){
const buttonRef=useRef(null)
return (
<div>
<Button2 ref={buttonRef}></Button2>
</div>
)
}
const Button2=(props)=>{
return <button {...props}/>
}
然后失败了
函数组件无法使用ref,你是不是要用用forwardRef?
你应该这么写
function App(){
const buttonRef=useRef(null)
return (
<div>
<Button3 ref={buttonRef}></Button3>
</div>
)
}
const Button2=(props,+ref)=>{
return <button +ref={ref} {...props}/>
}
+const Button3=React.forwardRef(Button2)
所以,如果你的函数组件,想要接受从外面传来的ref,那么子组件一定要用forwardRef包裹
useImperativeHandle
名字起的稀烂
应该叫做setRef
还是刚才的ref
这个buttonRef其实就是dom对象的引用,如果我希望得到的ref不是button呢
function App() {
const buttonRef = useRef(null);
useEffect(() => {
console.log(buttonRef.current);
});
return (
<div className="App">
<Button2 ref={buttonRef}>按钮</Button2>
<button
className="close"
onClick={() => {
console.log(buttonRef);
buttonRef.current.x();
}}
>
x
</button>
</div>
);
}
const Button2 = React.forwardRef((props, ref) => {
const realButton = createRef(null);
const setRef = useImperativeHandle;
setRef(ref, () => {
return {
x: () => {
realButton.current.remove();
},
realButton: realButton
};
});
return <button ref={realButton} {...props} />;
});
ref可以支持自定义,这里setRef将一个button给改成了一个空的函数
返回的原来的button
这玩意用的地方特别少
自定义hook
这里使用两个自定义hook的例子来介绍一下如何自定义一个hooks pai
例子一:我们使用useEffect去模拟componentDidMount生命周期,会发现第一次进入的时候也会输出结果,因为第一次渲染也改变了n的值
我不想它第一次渲染,怎么做呢
以点击加一的demo为例,自定义一个第一次更新不log,后面更新都log的hook
代码:
const useUpdate=(fn,dep)=>{
//自定义hooks 第一次更新不log后面的更新都log
const [count,setCount]=useState(0)
useEffect(()=>{
setCount(x=>x+1)
},[dep])
useEffect(()=>{
if(count>1){
fn()
}
},[count,fn])
}
export default useUpdate
原理,使用两次useEffect hook,因为useEffect 第一次更新就会log,所以我们监听另外一个数据,这个数据当原来的数据更新了之后会加一,当这个数据大于1之后,我们再调用useEffect 进行更新,这样第一次原本的数据更新的时候就不会log了。
例子二:
我们要创建一个人员的列表,需要在2s钟后加载完数据,未加载的时候显示‘加载中...’
不难想到,我们封装一个hooks,里面请求ajax就完事了
const useList = () => {
const [list, setList] = useState(null);
useEffect(() => {
ajax("/list").then(list => {
setList(list);
});
}, []); // [] 确保只在第一次运行
return {
list: list,
setList: setList
};
};
甚至想要加点功能,我还想要能添加和删除人员
多暴露接口就可以实现了
const useList = () => {
const [list, setList] = useState(null);
useEffect(() => {
ajax("/list").then(list => {
setList(list);
});
}, []); // [] 确保只在第一次运行
return {
list: list,
addItem: name => {
setList([...list, { id: Math.random(), name: name }]);
},
deleteIndex: index => {
setList(list.slice(0, index).concat(list.slice(index + 1)));
}
};
};
不难看出自定义hooks的方法,要什么功能,将对应的函数封装进去,暴露一个读接口,一个写接口就好了
总结
学习react hooks本质上在学习react hooks api
对照着图,能快速的想出每个api的用法,功能,使用场景,就差不多了