taro中hook的使用

Hook 是 React 16.8 的新增特性。它可以让你在不编写 class 的情况下使用 state 以及其他的 React 特性。

注意:要启用 Hook,所有 React 相关的 package 都必须升级到 16.8.0 或更高版本

Hook 这个单词的意思是"钩子"。

React Hooks 的意思是,组件尽量写成纯函数,如果需要外部功能和副作用,就用钩子把外部代码"钩"进来。 React Hooks 就是那些钩子。

下面就来介绍下hooks常用的钩子。。。

1. useState():状态钩子

useState()用于为函数组件引入状态(state)。纯函数不能有状态,所以把状态放在钩子里面。

实例

import { View, Text } from "@tarojs/components";
import { useState } from "react";

export default function HooksPage() {
  // 声明一个叫 "count" 的 state 变量
  let [count, setCount] = useState(0);
  // let [count, setCount] = useState<number>(0);

  return (
    <View>
        {/* useState实例 */}
      <View>
        <Text>useState实例:</Text>
        <Text
          onClick={() => {
            setCount(count + 1);
          }}
        >
          点击({count})
        </Text>
      </View>

    </View>
  );
}


小结

  1. useState()中的参数就是定义变量的初始值
  2. useState()返回的是一个数组,数组的第一个值为变量,就是上面实例中的count;第二个值是一个来更新这个count变量的方法(方法名为:set+变量名,例如上面实例中的setCount)
  3. useState()还可以给这个定义的变量规定一个数据类型,如下:
  let [count, setCount] = useState<number>(0);

如此定义,即count的数据类型必须得是number,如果是字符串类型,则就会报错。

2. useContext():共享状态钩子

组件之间共享状态,则可以使用useContext()

1.创建共享内容

既然是来共享状态的,那么我们首先要先创建共享的内容,创建一个useTestController.ts文件,里面放如下内容:

import { createContext, useState } from "react";

// 定义公用参数,方法
export const useTestController = ()=>{
    let [count, setCount] = useState(0)
    return {
        count,
        setCount
    }
}

// 定义的参数,方法类型规定
export interface TestContextType {
    count: number,
    setCount: Function
}

// 建一个context用来共享参数和方法
export const TestContext = createContext<TestContextType>({
    count: 0,
    setCount: (count:number) => {return count}
});

因为是多个组件共享这些参数和方法,所以我们在建立这些参数和方法的时候,最好在单独的一个文件夹里面放这些参数和方法,这样也便于后期维护。

2.创建共享组件

步骤1中的共享内容创建好了之后,我们再来创建我们需要共享这些内容的组件,创建共享组件之前,我们肯定得先写个页面来放这些组件,所以,先建立一个index.tsx,具体代码如下:

import Test1 from "./test1";
import Test2 from "./test2";

// 引入context
import {TestContext} from "./hooks/useTestController"
// 引入共享参数
import {useTestController} from "./hooks/useTestController"

export default function UseContext(){
    return (
        <TestContext.Provider value={useTestController()}>
            <Test1 />
            <Test2 />
        </TestContext.Provider>
       
    );
}

Text1.tsx组件源码如下:

import { View } from "@tarojs/components";
import { useContext } from "react";

// 引入context
import { TestContext } from "./hooks/useTestController";

export default function Test1() {
    const { count, setCount } = useContext(TestContext);
    return (
      <View
        onClick={() => {
          setCount(count + 1);
        }}
      >
        {count}
      </View>
    );
}

Text2.tsx组件源码如下:

import { View } from "@tarojs/components";
import { useContext } from "react";

// 引入context
import { TestContext } from "./hooks/useTestController";

// 引入test3组件
import Test3 from "./test3"

export default function Test2() {
  const { count, setCount } = useContext(TestContext);
  return (
    <View
      onClick={() => {
        setCount(count + 1);
      }}
    >
      {count}
      <Test3 />
    </View>
  );
}

Text3.tsx组件源码如下:

import { View } from "@tarojs/components";
import { useContext } from "react";

// 引入context
import { TestContext } from "./hooks/useTestController";

export default function Test2() {
  const { count, setCount } = useContext(TestContext);
  return (
    <View
      onClick={() => {
        setCount(count + 1);
      }}
    >
      {count}
    </View>
  );
}

通过useContext就可以实现多个组件共享状态,并且组件间复杂的嵌套组件也能共享,为父子传参提供了便利。

小结

实现组件之间的共享,其实就是简单的3个步骤:

  1. 先在外部创建一个context
const TextContext = React.createContext({});
  1. 然后再进行组件封装
 <TestContext.Provider value={{a:1}}>
    <Test1 />
    <Test2 />
</TestContext.Provider>
  1. 接着就在内部共享组件中,通过useContext获取共享参数,Test1代码如下
const Test1 = () => {
  const { a } = useContext(TestContext);
  return (
    <View>
       {a}
    </View>
  );
}

Test2和Test1获取共享内容的方式一样,通过以上三步就可以实现组件间的数据共享了。。。

3. useReducer():action 钩子

组件发出 action 与状态管理器通信,状态管理器收到 action 以后,使用 Reducer 函数算出新的状态

Reducer 函数的形式是(state, action) => newState

实例

import { View } from "@tarojs/components";
import { useReducer } from "react";

/**
 * reducer函数
 * */
const myReducer = (state, action) => {
  switch (action.type) {
    case "countUp":
      return {
        ...state,
        count: state.count + 1,
      };
    default:
      return state;
  }
};

export default function UseReducerComponent() {
  const [state, dispatch] = useReducer(myReducer, { count: 0 });
  return (
    <View onClick={() => dispatch({ type: "countUp" })}>{state.count}</View>
  );
}

小结

  1. 对于复杂的state操作逻辑,嵌套的state的对象,推荐使用useReducer
  2. useReducer的参数有两个,分别是reducer函数和state初始值
  3. useReducer返回一个数组,第一个值为状态当前的值,第二个值是发送 action 的dispatch函数

4. useEffect():副作用钩子

useEffect()用来引入具有副作用的操作,最常见的就是向服务器请求数据。

useEffect()接受两个参数。第一个参数是一个函数,异步操作的代码放在里面。第二个参数是一个数组,用于给出 Effect 的依赖项,只要这个数组发生变化,useEffect()就会执行。第二个参数可以省略,这时每次组件渲染时,就会执行useEffect()。

实例

import { View } from "@tarojs/components";
import { useEffect } from "react";

export default function UseEffectComponent(){
    let id = 1;
    useEffect(()=>{
       // 异步请求操作
    }, [id])
    return (
        <View>123</View>
    )
}

每当id发生变化时,useEffect都会执行;组件第一次渲染时,useEffect()也会执行。

5. useCallback

useCallback简单来说就是返回一个函数,只有在依赖项发生变化的时候才会更新(返回一个新的函数)。

const memoizedCallback = useCallback(
  () => {
    doSomething(a, b);
  },
  [a, b],
);

实例

import { View } from "@tarojs/components";
import { useCallback, useState } from "react";


export default function UseCallbackComponent(){
    let [count, setCount] = useState(0)
    let [count1, setCount1] = useState(0)

    const onClick = useCallback(()=>{
      setCount(count+1);
    }, [count1])

    const onClickNormal = ()=>{
        setCount1(count1+1)
    }
    return (
       <View>
            <View onClick={onClick}>useCallback:{count}</View>
            <View onClick={onClickNormal}>onClickNormal:{count1}</View>
       </View>
    )
}

上面实例中,只有当count1的值改变的时候,去执行onClick事件才能生效;如果count1没有改变,则onClick只会执行一次

小结

  1. useCallback参数有两个,第一个是回调函数,第二个是依赖参数;与useEffect类似,[]内可以放入你改变数值就重新渲染参数(函数)的对象。如果[]为空就是只渲染一次,之后都不会渲染。
  2. useCallback是使参数(函数)不会因为其他不相关的参数变化而重新渲染。

6. useMemo

useMemo、useCallback都是使参数(函数)不会因为其他不相关的参数变化而重新渲染。

useCallback(fn, deps) 相当于 useMemo(() => fn, deps)

import { View } from "@tarojs/components";
import { useMemo, useState } from "react";


export default function UseMemoComponent(){
    let [count, setCount] = useState(100)
    let [count1, setCount1] = useState(0)

    const memoCount = useMemo(()=>{
      setCount(count+1);
      return count;
    }, [count1])

    const onClickNormal = ()=>{
        setCount1(count1+1)
    }
    return (
       <View>
            <View>useMemo:{memoCount}</View>
            <View onClick={onClickNormal}>onClickNormal:{count1}</View>
       </View>
    )
}

小结

  1. useMemo与useCallback都是用来避免重复渲染,优化性能的,不同的是useMemo返回的是个数值,而useCallback返回的是个函数
  2. 切记: 传入 useMemo 的函数会在渲染期间执行。请不要在这个函数内部执行与渲染无关的操作,诸如副作用这类的操作属于 useEffect 的适用范畴,而不是 useMemo。

7. useRef

useRef 返回一个可变的 ref 对象,其 .current 属性被初始化为传入的参数(initialValue)。返回的 ref 对象在组件的整个生命周期内保持不变。

const refContainer = useRef(initialValue);

实例1

import { View, Text } from "@tarojs/components";
import { useRef, useState } from "react";

export default function UseRefComponent() {
  let [count, setCount] = useState(0);
//   let testRef = useRef<number>();
  let a = window.setTimeout(() => {
    setCount(count+1);
    // clearTimeout(testRef.current)
    clearTimeout(a);
  }, 1000);

  return (
    <View>
      <Text>{count}</Text>
    </View>
  );
}

实例1中的定时器,因为state每次更新一下,页面都会重新渲染,定时器参数a也会新生成一个,所以定时器关不掉,这个时候就要用到useRef了,具体看如下实例2

实例2

import { View, Text } from "@tarojs/components";
import { useRef, useState } from "react";

export default function UseRefComponent() {
  let [count, setCount] = useState(0);
  let testRef = useRef<number>();
  testRef.current = window.setTimeout(() => {
    setCount(count+1);
    clearTimeout(testRef.current)
  }, 1000);

  return (
    <View>
      <Text>{count}</Text>
    </View>
  );
}

实例2中用到useRef,哪怕state更新会渲染更新页面,但是每次渲染返回的都是同一个ref对象,所有就可以用useRef来定义定时器,这样就可以彻底删掉这个定时器

小结

  1. useRef是一个方法,且useRef返回一个可变的ref对象(对象!!!)
  2. initialValue被赋值给其返回值的.current对象
  3. 可以保存任何类型的值:dom、对象等任何可辨值
  4. ref对象与自建一个{current:‘’}对象的区别是:useRef会在每次渲染时返回同一个ref对象,即返回的ref对象在组件的整个生命周期内保持不变。自建对象每次渲染时都建立一个新的。
  5. ref对象的值发生改变之后,不会触发组件重新渲染。
  6. 本质上,useRef就是一个其.current属性保存着一个可变值“盒子”。
  7. useRef() 比 ref 属性更有用。它可以很方便地保存任何可变值,其类似于在 class 中使用实例字段的方式。

8. useImperativeHandle

useImperativeHandle主要作用是减少暴露给父组件获取的DOM元素属性, 子组件只暴露给父组件需要用到的DOM方法

useImperativeHandle(ref, createHandle, [deps])

实例

父组件代码如下:

import { View } from "@tarojs/components";
import { useRef } from "react";
import ChildComponent from "./child"

export default function UseImperativeHandleComponet(){
    let testRef = useRef({
        onClick: () => undefined
    });

   return (
       <View>
           <View onClick={()=>{testRef.current.onClick()}}>点击我,调用子组件方法</View>
           <ChildComponent ref={testRef} />
       </View>
   )
}

子组件代码如下:

import { View } from "@tarojs/components";
import { useImperativeHandle, forwardRef, useState } from "react";

export default forwardRef((props, ref)=>{

    let [count, setCount] = useState(0);

    const onClick = ()=>{
        setCount(count+1);
    }

    // 参数1: 父组件传递的ref属性
    // 参数2: 返回一个对象,父组件通过ref.current调用对象中方法
    useImperativeHandle(ref, () => ({
       onClick
      }))

    return (
        <View onClick={onClick}>{count}</View>
    )
})

小结

  1. useImperativeHandle一般和forwardRef一起使用
  2. useImperativeHandle第一个参数是父组件传递的ref属性;第二个参数是返回一个对象,父组件通过ref.current来调用对象中的方法;第三个参数是数组,依赖改变了,useImperativeHandle就更新了
  3. useImperativeHandle的作用就是父组件能够调用子组件的方法

9. useLayoutEffect

useLayoutEffect是用在处理DOM的时候,当你的useEffect里面的操作需要处理DOM,并且会改变页面的样式,就需要用这个,否则可能会出现出现闪屏问题, useLayoutEffect里面的callback函数会在DOM更新完成后立即执行,但是会在浏览器进行任何绘制之前运行完成,阻塞了浏览器的绘制.

10. useDebugValue

useDebugValue(value)

useDebugValue 可用于在 React 开发者工具中显示自定义 hook 的标签。

11. 自定义hook

当我们想在两个函数之间共享逻辑时,我们会把它提取到第三个函数中。而组件和 Hook 都是函数,所以也同样适用这种方式。

自定义 Hook 是一个函数,其名称以 “use” 开头,函数内部可以调用其他的 Hook

实例

以下是一个倒计时hooks实例

在useTestHook.ts文件中:

import { useRef, useState } from "react";
/**
 * 倒计时hook
 * @param count 秒数
 */
export default function(count:number) {
  let [countDown, setCountDown] = useState(count)
  let timer = useRef<number>();
  timer.current = window.setTimeout(() => {
    if (countDown > 0) {
        countDown--;
        setCountDown(countDown)
    } else {
      clearTimeout(timer.current);
    }
 
  }, 1000);

  return countDown;
};

index.tsx中引入这个hooks:

import { View } from "@tarojs/components";

// 引入自定义hooks
import useTestConstr from "./hooks/useTestHook"

export default function CustomHook(){
 
    return (
        <View>倒计时:{useTestConstr(60)}s</View>
    )
}

关于在taro中hooks的使用,暂时先写到这里,后面如果有新的发现会不定时编辑更新哈

  • 1
    点赞
  • 8
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值