React Hook 使用

React Hook 使用

本文章主要介绍如下hook:useState, useEffect, memo, useCallback, useMemo。

useState

useState可以让你在不编写 class 的情况下使用 state 以及其他的 React 特性。

import React, { useState, useEffect, memo, useCallback, useMemo } from "react";
import { Button } from "antd";

const Main = () => {
  const [count, setCount] = useState(0);

  return (
    <div>
      <p>count:{count}</p>
      <Button onClick={() => { setCount(count + 1) }}>cllick(count)</Button>
    </div>
  );
};

export default Main;

useEffect

Effect Hook 可以让你在函数组件中执行副作用操作,你可以把 useEffect Hook 看做 componentDidMount,componentDidUpdate 和 componentWillUnmount 这三个函数的组合。

import React, { useState, useEffect, memo, useCallback, useMemo } from "react";
import { Button } from "antd";

const Main = () => {
  const [count, setCount] = useState(0);

  useEffect(() => {
    console.log(`You clicked ${count} times.`);
    // useEffect 的第二个参数是数组
    // [count] 表示 count 与原来的值不同时,才会调用回调函数
  }, [count]);
 
  return (
    <div>
      <p>count:{count}</p>
      <Button onClick={() => { setCount(count + 1) }}>cllick(count)</Button>
    </div>
  );
};

export default Main;

此时的useEffect相当于componentDidMount与componentDidUpdate,在render完成以及state,props改变时会执行里面的回调函数。

当 useEffect 的 effect 返回一个函数,React 将会在执行清除操作时调用它,也即相当于componentWillUnmount:

  useEffect(() => {
    console.log(`You clicked ${count} times.`);
    return () => {
      console.log('componentWillUnmount');
    }
  }, [count]);

React.memo()

先抛出一个问题:React 中当组件的 props 或 state 改变时,当前组件及其子组件会重新 render,即使子组件没有用到 props 或 state,看下面的例子。

import React, { useState, useEffect, useCallback, useMemo } from "react";
import { Button } from "antd";

const Child = function () {
  useEffect(() => {
    console.log("Child render.");
  };
  return (<div>123</div>);
};

const Main = () => {
  const [count, setCount] = useState(0);

  return (
    <div>
      <div>
        <p>
          <Button
            onClick={() => {
              setCount(count + 1);
            }}
          >
            click(count)
          </Button>
        </p>
        <p>count: {count}</p>
      </div>
      <Child />
    </div>
  );
};

export default Main;

点击父组件中按钮,会修改 count 变量的值,进而导致父组件重新渲染,此时子组件压根没有任何变化(props、state),从下图可以看到,Child重新渲染了。
在这里插入图片描述
使用 memo 即可以解决:

import React, { memo } from "react";

const Child = memo(function () {
  useEffect(() => {
    console.log("Child render.");
  };
  return (<div>123</div>);
});

在这里插入图片描述
这种写法是 React 的高阶组件写法,将组件作为memo()函数的参数,函数的返回值是一个新的组件。

useCallback

当前Child没有props传入,我们传入props看看:

import React, { useState, useEffect, memo, useCallback, useMemo } from "react";
import { Button } from "antd";

interface ICProps {
  name: string;
  onChangeName: React.MouseEventHandler<HTMLElement>;
}

const Child = memo(function (props: ICProps) {
  const { onChangeName, name } = props;
  useEffect(() => {
    console.log("Child render.");
  });
  return (
    <div>
      <p>name: {name}</p>
      <p>
        <Button onClick={onChangeName}>click name</Button>
      </p>
    </div>
  );
});

const Main = (props: IProps) => {
  const [count, setCount] = useState(0);
  const [name, setName] = useState("sarah");

  const onChangeName = () => { setName("jack"); };

  return (
    <div>
      <div>
        parent:
        <p>
          <Button
            onClick={() => {
              setCount(count + 1);
            }}
          >
            click(count)
          </Button>
        </p>
        <p>
          count: {count}
        </p>
      </div>
      <hr />
      child:
      <Child onChangeName={onChangeName} name={name} />
    </div>
  );
};

export default Main;

Child 组件传入props:onChangeName,name。当count改变时,发现Child会重新渲染。
在这里插入图片描述
我们使用了memo,Child依然渲染了,说明Child的props被改变了。

分析原因:

  • 点击父组件按钮,改变了父组件中 count 变量值(父组件的 state 值),进而导致父组件重新渲染;
  • 父组件重新渲染时,会重新创建 onChangeName 函数,即传给子组件的 onChangeName props 发生了变化,导致子组件渲染;

使用 useCallback 解决:

const Main = (props: IProps) => {
  const [count, setCount] = useState(0);
  const [name, setName] = useState("sarah");

  // useCallback 第二个参数传 [],则无论 state 怎么变,useCallback 只会执行一次
  // 这就告诉 React 你的 effect 不依赖于 props 或 state 中的任何值,所以它永远都不需要重复执行。
  // 故 onChangeName 不会再次改变
  const onChangeName = useCallback(() => {
    setName("jack");
  }, []);

  return (
    // ...
  );
};

上面代码并不需要 onChangeName 函数,抽离出 onChangeName 函数是为了演示,父组件重新渲染时,会重新创建 onChangeName 函数。

同理,如果在父组件定义其他类型 a,将 a 传入子组件,那么父组件重新渲染时,是否会重新创建 a?

测试发现,只有引用数据类型,才会重新创建,useMemo()便是用于解决此问题的。

useMemo

import React, { useState, useEffect, memo, useCallback, useMemo } from "react";
import { Button } from "antd";


interface ICProps {
  name: string;
  onChangeName: React.MouseEventHandler<HTMLElement>;
  info: { count: number; };
}

const Child = memo(function (props: ICProps) {
  const { onChangeName, name } = props;
  useEffect(() => {
    console.log("Child render.");
  });
  return (
    <div>
      <p>name: {name}</p>
      <p>
        <Button onClick={onChangeName}>click name</Button>
      </p>
    </div>
  );
});

const Main = (props: IProps) => {
  const [count, setCount] = useState(0);
  const [name, setName] = useState("sarah");

  const info = { count: 18 };

  return (
    <div>
      <div>
        parent:
        <p>
          <Button
            onClick={() => {
              setCount(count + 1);
            }}
          >
            click(count)
          </Button>
        </p>
        <p>
          count: {count}
        </p>
      </div>
      <hr />
      child:
      <Child onChangeName={onChangeName} name={name} info={info} />
    </div>
  );
};

export default Main;

在这里插入图片描述
useMemo 有两个参数:

  • 第一个参数是个函数,返回的对象指向同一个引用,不会创建新对象;
  • 第二个参数是个数组,只有数组中的变量改变时,第一个参数的函数才会返回一个新的对象。
  const info = useMemo(() => ({ name, age }), [name, age])   // 包一层

再次点击count不会重新渲染Child。

React中,使用上下文(context)可以方便地在组件之间共享数据。通过使用ReactHooks中的useContext钩子,我们可以更简单地使用上下文。 首先,我们需要创建一个上下文对象,可以使用React的createContext函数来创建。这个上下文对象可以包含我们想要共享的任何类型的数据。 然后,在函数组件中使用useContext钩子来读取上下文的值。使用useContext,我们需要将上下文对象作为参数传递给它,它将返回上下文的当前值。 需要注意的是,尽管可以使用useContext读取上下文的值,但我们仍然需要在上层组件树中使用<MyContext.Provider>来为下层组件提供上下文。这个<MyContext.Provider>组件接收一个value属性,用于传递上下文的值给子组件。 下面是一个使用原始写法和使用context hook写法的例子: 原始写法: ``` import React from 'react'; // 创建一个上下文,初始化值 const MyContext = React.createContext({name: 'twinkle', age: 18}); // 文本组件 function TextComp() { return ( <> <MyContext.Consumer> {value => ( <> <p>名字:{value.name}</p> <p>年龄:{value.age}</p> </> )} </MyContext.Consumer> </> ); } // 测试组件 export default function TestContextComp() { return ( <div> <MyContext.Provider value={{name: 'cll', age: 18}}> <TextComp/> </MyContext.Provider> </div> ); } ``` 使用context hook写法: ``` import React, { useContext } from 'react'; // 上下文数据的结构 interface ICtxP { name: string; age: number; } // 创建一个上下文 const MyContext = React.createContext<ICtxP>({ name: 'twinkle', age: 18 }); // 文本组件 function TextComp() { const value = useContext(MyContext); // 使用上下文hook return ( <> <p>名字: {value.name}</p> <p>年龄: {value.age}</p> </> ); } export default function TextContextHook() { return ( <div> <MyContext.Provider value={{ name: 'cll', age: 18 }}> <TextComp/> </MyContext.Provider> </div> ); } ``` 无论是原始写法还是使用context hook写法,它们都可以实现在React使用上下文来共享数据。使用context hook写法可以减少代码量并简化组件的结构,使其更加简洁和易于理解。<span class="em">1</span><span class="em">2</span><span class="em">3</span> #### 引用[.reference_title] - *1* *2* *3* [react 进阶hook 之 context Hook](https://blog.csdn.net/qq_41499782/article/details/115493691)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v93^chatsearchT3_2"}}] [.reference_item style="max-width: 100%"] [ .reference_list ]
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

北筏丶

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值