4
头图

快来加入我们吧!

"小和山的菜鸟们",为前端开发者提供技术相关资讯以及系列基础文章。为更好的用户体验,请您移至我们官网小和山的菜鸟们 ( https://xhs-rookies.com/ ) 进行学习,及时获取最新文章。

"Code tailor" ,如果您对我们文章感兴趣、或是想提一些建议,微信关注 “小和山的菜鸟们” 公众号,与我们取的联系,您也可以在微信上观看我们的文章。每一个建议或是赞同都是对我们极大的鼓励!

我们为什么要学 hooks

React-Hooks 简介

react 为什么要有一个 hooks?

1.有状态的类组件的复用太麻烦

react 的核心思想就是,将一个页面拆成一堆独立的,可复用的组件,并且用自上而下的单向数据流的形式将这些组件串联起来。但如果你在大型的工作项目中用 react,你会发现你的项目中实际上很多 react 组件冗长且难以复用。特别是那些 class 的组件,它们本身包含了 state,很难进行复用。

官方推荐解决方案

  • 渲染属性 - 使用一个值为函数的 prop 来传递需要动态渲染的 nodes 或组件.如下面的代码可以看到我们的Provider组件包含了所有跟状态相关的代码,而 MyComponent 组件则可以是一个单纯的展示型组件,这样一来Provider就可以单独复用了
import MyComponent from 'components/myComponent'
class Provider extends React.Component {
  constructor(props) {
    super(props)
    this.state = { target: 'MyComponent' }
  }

  render() {
    return <div>{this.props.render(this.state)}</div>
  }
}

<Provider render={(data) => <MyComponent target={data.target} />} />

这个模式叫 Render-Props .

当然一般情况下,都会被写成下面这样的方式:

<Provider>{(data) => <Cat target={data.target} />}</Provider>
  • HOC 高阶组件 - 一个函数接受一个组件作为参数,经过一系列加工后,最后返回一个新的组件.看下面的代码示例,withUser 函数就是一个高阶组件,它返回了一个新的组件,这个组件具有了它提供的获取用户信息的功能。
const withUser = (WrappedComponent) => {
  const user = localStorage.getItem('user')
  return (props) => <WrappedComponent user={user} {...props} />
}

const UserPage = (props) => (
  <div class="user-page">
    <p>I'm the user, {props.user}!</p>
  </div>
)

export default withUser(UserPage)

以上这两种模式看上去都挺不错的,很多库也运用了这样的模式,就像我们常用的 React-router 库。但是这两种模式,会增加代码的层级关系。为了表现的明显,可以安装打开 React Devtools 看看代码的组件嵌套,会发现嵌套次数太多太多。

而如果我们使用 hooks ,那就会简洁很多,没有多余的层级嵌套。把各种想要的功能写成一个一个可复用的自定义 hook,当你的组件想用什么功能时,直接在组件里调用这个 hook 即可。

2.生命周期函数里面逻辑比较复杂

我们通常希望一个函数只做一件事情,但我们的生命周期钩子函数里通常同时做了很多事情。比如我们需要在 componentDidMount 中发起请求获取数据,绑定一些事件监听等等。同时,有时候我们还需要在 componentDidUpdate 做一遍同样的事情。

当我们的这个页面或者这个组件,变得复杂的时候,里面的内容就会变多,逻辑的清晰度就会下降。

3.class 中的 this 指向问题

父组件给子组件传递函数时, 必须绑定 this

  • react 中的组件四种绑定 this 方法的区别
class App extends React.Component<any, any> {
  handleClick2

  constructor(props) {
    super(props)
    this.state = {
      num: 1,
      title: ' react study',
    }
    this.handleClick2 = this.handleClick1.bind(this)
  }

  handleClick1() {
    this.setState({
      num: this.state.num + 1,
    })
  }

  handleClick3 = () => {
    this.setState({
      num: this.state.num + 1,
    })
  }

  render() {
    return (
      <div>
        <h2>Ann, {this.state.num}</h2>
        <button onClick={this.handleClick2}>btn1</button>
        <button onClick={this.handleClick1.bind(this)}>btn2</button>
        <button onClick={() => this.handleClick1()}>btn3</button>
        <button onClick={this.handleClick3}>btn4</button>
      </div>
    )
  }
}
  • 构造函数中绑定 this,那么每次父组件刷新的时候,如果传递给子组件其他的 props 值不变,那么子组件就不会刷新
  • render() 函数里面绑定 this:因为 bind 函数会返回一个新的函数,所以每次父组件刷新时,都会重新生成一个函数,即使父组件传递给子组件其他的 props 值不变,子组件每次都会刷新;
  • ()=>{} 箭头函数:父组件刷新的时候,即使两个箭头函数的函数体是一样的,都会生成一个新的箭头函数,所以子组件每次都会刷新;
  • 使用类的静态属性:原理和第一种方法差不多,比第一种更简洁

综上所述,如果不注意的话,很容易写成第三种写法,导致性能上有所损耗

hooks 优点

  • 能优化类组件存在问题
  • 能在无需修改组件结构的情况下复用状态逻辑(自定义 Hooks
  • 能将组件中相互关联的部分拆分成更小的函数(比如设置订阅或请求数据)
  • 副作用的关注点分离副作用指那些没有发生在数据向视图转换过程中的逻辑,如 ajax 请求、访问原生dom 元素、本地持久化缓存、绑定/解绑事件、添加订阅、设置定时器、记录日志等。以往这些副作用都是写在类组件生命周期函数中的。而 useEffect 在全部渲染完毕后才会执行,useLayoutEffect 会在浏览器 layout 之后,painting 之前执行。

小结

现在,我们对 hooks 已经有了一个大概的了解。

那么之后就开始我们的基础 hooks 教程了。

hooks 系列中,我们主要介绍四个项目中常用的钩子:useStateuseEffectuseRefsuseCallback.

如果你们想要了解一些其他钩子函数(useContextuseReduceruseMemouseImperativeMethodsuseMutationEffectuseLayoutEffect),可以去官网查看

下节预告

在下节中,我们将为大家介绍 useState ,敬请期待!


小和山的菜鸟们
377 声望2.1k 粉丝

每日进步的菜鸟,分享前端学习手册,和有心学习前端技术的小伙伴们互相探讨,一同成长。