react 点击两次_React学习之路-react hooks总结(下)

9e1d6ec344d79fdbc66c6af122d9c503.png

这里承接上一片博客来总结其他的几个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}/>
}

然后失败了

ca51d0795ef6695028ed17cc8d429696.png

函数组件无法使用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给改成了一个空的函数

90b408b195e0222cae2584dbb6b73046.png

返回的原来的button

8343b968984905d97ccbf0031688878c.png

这玩意用的地方特别少


自定义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

0ad43ae78896745de5c25326829b2e3e.png

对照着图,能快速的想出每个api的用法,功能,使用场景,就差不多了

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值