快来加入我们吧!
"小和山的菜鸟们",为前端开发者提供技术相关资讯以及系列基础文章。为更好的用户体验,请您移至我们官网小和山的菜鸟们 ( xhs-rookies.com/ ) 进行学习,及时获取最新文章。
"Code tailor" ,如果您对我们文章感兴趣、或是想提一些建议,微信关注 “小和山的菜鸟们” 公众号,与我们取的联系,您也可以在微信上观看我们的文章。每一个建议或是赞同都是对我们极大的鼓励!
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 系列中,我们主要介绍四个项目中常用的钩子:useState、useEffect、useRefs、useCallback.
如果你们想要了解一些其他钩子函数(useContext 、useReducer 、 useMemo 、 useImperativeMethods 、useMutationEffect、 useLayoutEffect),可以去官网查看。
下节预告
在下节中,我们将为大家介绍 useState ,敬请期待!