您当前的位置: 首页 > 技术文章 > 移动开发

react hook 详解及使用

作者: 时间:2022-06-23阅读数: 人阅读

1、hooks 是什么

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

Hook 是一些可以让你在函数组件里“钩入” React state 及生命周期等特性的函数。Hook 不能在 class 组件中使用 —— 这使得你不使用 class 也能使用 React。

2、为什么要使用 hooks

  • 难以理解的 class 、组件中必须去理解 javascript 与 this 的工作方式、需要绑定事件处理器、纯函数组件与 class 组件的差异存在分歧、甚至需要区分两种组件的使用场景;Hook 可以使你在非 class 的情况下可以使用更多的 React 特性。

  • 在组件之间复用状态逻辑很难、大型组件很难拆分和重构,也很难测试。Hook 使你在无需修改组件结构的情况下复用状态逻辑。

  • 复杂组件变得难以理解、业务逻辑分散在组件的各个方法之中,导致重复逻辑或关联逻辑。Hook 将组件中相互关联的部分拆分成更小的函数(比如设置订阅或请求数据)

3、react 中我们常用的 hook

什么是 Hook?

Hook 是一些可以让你在函数组件里“钩入” React state 及生命周期等特性的函数。Hook 不能在 class 组件中使用 —— 这使得你不使用 class 也能使用 React。

下面我们挑几种介绍一下(useState、userContext、useEffect )

1) useState 状态钩子

import React, { useState } from 'react';

function Example() {
  // 声明一个叫 "count" 的 state 变量
  const [count, setCount] = useState(0);

  return (
    <div>
      <p>You clicked {count} times</p>
      <button onClick={() => setCount(count + 1)}>Click me</button>
    </div>
  );
}

等价的 class 示例

class Example extends React.Component {
  constructor(props) {
    super(props);
    this.state = {
      count: 0,
    };
  }

  render() {
    return (
      <div>
        <p>You clicked {this.state.count} times</p>
        <button onClick={() => this.setState({ count: this.state.count + 1 })}>Click me</button>
      </div>
    );
  }
}

什么时候我会用 Hook? 如果你在编写函数组件并意识到需要向其添加一些 state,以前的做法是必须将其它转化为 class。现在你可以在现有的函数组件中使用 Hook。

在 useState()中,它接受状态的初始值作为参数,即上例中计数的初始值,它返回一个数组,其中数组第一项为一个变量,指向状态的当前值。类似 this.state,第二项是一个函数,用来更新状态,类似 setState。

上述例子中没有 class 继承、没有 this、没有生命周期、代码更加简洁、这就是使用 hooks 的意义;

2)useContext() 共享状态钩子

如果需要在深层次组件之间共享状态,可以使用 useContext()。context 提供了一种在组件之间共享 props 的方式,而不必显示地通过组件树的逐层传递 props; useContext 钩子比原始 class 组件中使用 context 更为方便;

假设现在有俩个组件 Navbar 和 Messages,我们希望它们之间共享状态。

<div className="test">
  <Navbar />
  <Messages />
</div>

使用方法如下: 第一步在它们的父组件上使用 React 的 Context API,在组件外部建立一个 Context。

import React, { useContext } from 'react';

const TestContext = React.createContext({});

const Navbar = () => {
  const { username } = useContext(TestContext);

  return (
    <div className="navbar">
      <p>{username}</p>
    </div>
  );
};

const Messages = () => {
  const { messageDetail } = useContext(TestContext);

  return (
    <div className="messages">
      <p>1 message for {messageDetail}</p>
    </div>
  );
};

const App = () => {
  return (
    <TestContext.Provider
      value={{
        username: 'superawesome',
        messageDetail: 'hello world',
      }}
    >
      <div className="test">
        <Navbar />
        <Messages />
      </div>
    </TestContext.Provider>
  );
};
export default App;

等价的 class 用例

import React from 'react';

const TestContext = React.createContext({});

const Navbar = () => (
  <TestContext.Consumer>
    {({ username }) => (
      <div className="messages">
        <p>1 message for {username}</p>
      </div>
    )}
  </TestContext.Consumer>
);

const Messages = () => {
  return (
    <TestContext.Consumer>
      {({ messageDetail }) => (
        <div className="messages">
          <p>1 message for {messageDetail}</p>
        </div>
      )}
    </TestContext.Consumer>
  );
};

class App extends React.Component {
  render() {
    return (
      <TestContext.Provider
        value={{
          username: 'superawesome',
          messageDetail: 'hello world',
        }}
      >
        <div className="test">
          <Navbar />
          <Messages />
        </div>
      </TestContext.Provider>
    );
  }
}
export default App;

两种用法相比较 hooks 更加简介易懂、render 中也没有函数式写法!

3)useEffect 副作用钩子

它可以用来更好的处理副作用,如异步请求等;可以把 useEffect Hook 看做 componentDidMount,componentDidUpdate 和 componentWillUnmount 这三个函数的组合。

useEffect(() => {}, [array]);

使用 useEffect 更新网页 title

import React, { useState, useEffect } from 'react';

function Example() {
  const [count, setCount] = useState(0);

  // Similar to componentDidMount and componentDidUpdate:
  useEffect(() => {
    // Update the document title using the browser API
    document.title = `You clicked ${count} times`;
  },[count]);

  return (
    <div>
      <p>You clicked {count} times</p>
      <button onClick={() => setCount(count + 1)}>Click me</button>
    </div>
  );
}

使用 class (等价)

class Example extends React.Component {
  constructor(props) {
    super(props);
    this.state = {
      count: 0,
    };
  }

  componentDidMount() {
    document.title = `You clicked ${this.state.count} times`;
  }
  componentDidUpdate() {
    document.title = `You clicked ${this.state.count} times`;
  }

  render() {
    return (
      <div>
        <p>You clicked {this.state.count} times</p>
        <button onClick={() => this.setState({ count: this.state.count + 1 })}>Click me</button>
      </div>
    );
  }
}

useEffect()接受两个参数,第一个参数是你要进行的异步操作,第二个参数是一个数组,用来给出 Effect 的依赖项。只要这个数组发生变化,useEffect()就会执行。当第二项省略不填时,useEffect()会在每次组件渲染时执行。这一点类似于类组件的 componentDidMount。

我们实现一个 useEffect() 依赖项变化的例子

import React, { useState, useEffect } from 'react';

const AsyncCount = ({ countNum }) => {
  const [loading, setLoading] = useState(true);
  const [count, setCount] = useState(0);

  useEffect(() => {
    setLoading(true);
    setTimeout(() => {
      setLoading(false);
      setCount(countNum);
    }, 2000);
  }, [countNum]);
  return <>{loading ? <p>Loading...</p> : <p>{count}</p>}</>;
};

const TestCount = () => {
  const [count, setCount] = useState(0);
  const changeCount = (name) => {
    setCount(name);
  };
  return (
    <>
      <AsyncCount countNum={count} />
      <button
        onClick={() => {
          changeCount(count + 1);
        }}
      >
        增加
      </button>
      <button
        onClick={() => {
          changeCount(count - 1);
        }}
      >
        减少
      </button>
    </>
  );
};

export default TestCount;

再上述例子中,我们把处理 count 异步的操作以及是否渲染 loading,都放在了 AsyncCount hook 中;把复杂操作,通过 hooks 提取出去;将组件中关联部分拆分;

那下面我们在做一个更加细化的拆分,拆出一个自己的 hook

import React, { useState, useEffect } from 'react';

const useCount = (countNum) => {
  const [loading, setLoading] = useState(true);
  const [count, setCount] = useState(0);

  useEffect(() => {
    setLoading(true);
    setTimeout(() => {
      setLoading(false);
      setCount(countNum);
    }, 2000);
  }, [countNum]);
  return [loading, count];
};

const AsyncCount = ({ countNum }) => {
  const [loading, count] = useCount(countNum);
  return <>{loading ? <p>Loading...</p> : <p>{count}</p>}</>;
};

const TestCount = () => {
  const [count, setCount] = useState(0);
  const changeCount = (count) => {
    setCount(count);
  };
  return (
    <>
      <AsyncCount countNum={count} />
      <button
        onClick={() => {
          changeCount(count + 1);
        }}
      >
        增加
      </button>
      <button
        onClick={() => {
          changeCount(count - 1);
        }}
      >
        减少
      </button>
    </>
  );
};

export default TestCount;

我们在上述 AsyncCount 组件中再次将它的副作用操作拆分;在此组件中只关注渲染结果,useCount 接受一个数字,返回一个数组,数组中包括状态,与 count 两个结果;在我们使用 useCount 时,会根据我们传入参数的不同而返回不同的状态;

另外关于 hook 其他注意事项、在使用 useState 和一些其他钩子时、它们彼此的对应关系、是依靠 Hook 调用的顺序、所以我们不要在循环、条件、或嵌套函数中使用 hook、确保总是在你的 React 函数的最顶层调用他们,这让 React 能够在多次的 useState 和 useEffect 调用之间保持 hook 状态的正确;

用代码来解释一下顶层的意思

if (count !== 0) {
    useEffect(function persistForm() {
      ...
    });
}

这样会导致在 count 不等于 0 时,条件值为 true,会执行此次 hook,但如果在某次渲染时、如 count 值变为 0,那么此次 hook 将不在渲染,从而导致 hook 顺序发生了改变,产生不可预知的 bug;

其次在 hooks 中在 setState 值为 Object 或 Array 时,由于为引用类型,setState 通过回调函数的形式赋值,其参数值存的是 obj 的地址,由于 react 中 state 是只读的,所以在操作本身值改变是无法成功的,所以我们应当拷贝出去一份,在重新赋值;还有 hooks 异步获取当前 state 值问题等等,遇到问题多百度;

它为我们本来的开发带来了很大的变化、更加便于我们操作使用;come on!

本站所有文章、数据、图片均来自互联网,一切版权均归源网站或源作者所有。

如果侵犯了你的权益请来信告知我们删除。邮箱:licqi@yunshuaiweb.com

加载中~
如果您对我们的成果表示认同并且觉得对你有所帮助可以给我们捐赠。您的帮助是对我们最大的支持和动力!
捐赠我们
扫码支持 扫码支持
扫码捐赠,你说多少就多少
2
5
10
20
50
自定义
您当前余额:元
支付宝
微信
余额

打开支付宝扫一扫,即可进行扫码捐赠哦

打开微信扫一扫,即可进行扫码捐赠哦

打开QQ钱包扫一扫,即可进行扫码捐赠哦

天猫38节现货-全屋智能