内容简介:React Hooks是React 16.8发布以来最吸引人的特性之一。在开始介绍React Hooks之前,让咱们先来理解一下什么是hooks。wikipedia是这样给hook下定义的:In computer programming, the term hooking covers a range of techniques used to alter or augment the behaviour of an operating system, of applications, or of othe
React Hooks是React 16.8发布以来最吸引人的特性之一。在开始介绍React Hooks之前,让咱们先来理解一下什么是hooks。wikipedia是这样给hook下定义的:
In computer programming, the term hooking covers a range of techniques used to alter or augment the behaviour of an operating system, of applications, or of other software components by intercepting function calls or messages or events passed between software components. Code that handles such intercepted function calls, events or messages is called a hook .
通俗来说,Hook(钩子)就是通过拦截软件和系统内部函数调用和消息通信来增强原有功能的技术。而React Hooks想要增强哪些功能呢?设想你的项目中已经有一大堆组件,这些组件各自都拥有自己的状态。那么一旦你想重用某些特定的带状态逻辑,就得大幅度重构你的应用。现在有了React Hooks,你只需要抽离这些带状态的逻辑代码,然后它们可以更好地进行重用, 而且独立出来的代码也更容易进行测试和管理。有了React Hooks后,你可以在函数式组件中实现之前在带状态组件中能做到的任何事,你能够更灵活地实现你的应用代码。
接下来,让我们看看React Hooks在实际项目中到底怎么使用。
状态管理
对于业务性组件来说,状态管理肯定是不可避免的。以前,我们通常写Class组件来管理业务逻辑,或者使用redux来全局管理状态。现在我们可以利用React Hooks新提供的 State Hook
来处理状态,针对那些已经写好的Class组件,我们也可以利用 State Hook
很好地进行重构, 先来看下面这段代码:
import React from 'react'; class Person extends React.Component { constructor(props) { super(props); this.state = { username: "scq000" }; } render() { return ( <div> <p>Welcome to homepage. {state.username}</p> <input type="text" placeholder="input a username" onChange={(event) => this.setState({ username: event.target.value)})}></input> </div> ); } } 复制代码
接下来尝试将它重构成函数式组件:
import React, {useState} from 'react'; export const Person = () => { const [state, setState] = useState({username: "scq000"}); return ( <div> <p>Welcome to homepage. {state.username}</p> <input type="text" placeholder="input a username" onChange={(event) => setState({username: event.target.value})}></input> </div> ) } 复制代码
如上面这段代码,我们首先使用 useState
api 来声明一个内部状态,接着声明一个新的状态变量 state
,以及它的setter方法。在这里,为了减少重构的工作量我特意选择了 state
这个变量名,你也可以单独将每个独立的状态提取出来使用, 比如使用代码 const [username, setUsername] = userState("scq000")
。在随后的组件内部,我们就可以利用这个内部状态来处理业务逻辑了。由于是函数式组件的写法,我们也能够避免很多this绑定,而且这部分逻辑在后续使用过程中也可以抽离出来进行重用。不过这里有个需要注意的点是: 当你使用set方法的时候,旧状态不会自动merge到新状态中去
,所以你如果提取的状态是个对象,且有多个属性时,需要使用如下语法进行状态的更新:
setState({ ...state, username: event.target.value }); 复制代码
生命周期管理
我们都知道,组件的生命周期管理是整个react组件的灵魂所在。利用生命周期函数,我们可以控制整个组件的加载、更新和卸载。React Hooks中提供了 Effect
钩子,使我们可以在函数式组件中实现这些功能。
为了便于理解,接下来我将分别演示如何利用 Effect
钩子实现原本在Class组件中的各个生命周期方法。下面这段代码是我们熟悉的Class组件:
import React from 'react'; class Person extends React.Component { constructor(props) { super(props); this.state = { username: "scq000" }; } componentDidMount() { console.log('componentDidMount: 组件加载后') } componentWillUnmount() { console.log('componentWillUnmount: 组件卸载, 做一些清理工作') } componentDidUpdate(prevProps, prevState) { if(prevState.username !== this.state.username) { console.log('componentDidUpdate: 更新usernmae') } } render() { return ( <div> <p>Welcome to homepage. {state.username}</p> <input type="text" placeholder="input a username" onChange={(event) => this.setState({ username: event.target.value)})}></input> </div> ); } } 复制代码
现在我们利用 Effect
重构一下:
import React, {useState, useEffect} from 'react'; export const Person = () => { const [state, setState] = useState({username: "scq000"}); useEffect(() => { console.log('componentDidMount: 组件加载后') return () => { console.log('componentWillUnmount: 组件卸载, 做一些清理工作') } }, []); useEffect(() => { console.log('componentDidUpdate: 更新usernmae') }, [state.username]); return ( <div> <p>Welcome to homepage. {state.username}</p> <input type="text" placeholder="input a username" onChange={(event) => setState({username: event.target.value})}></input> </div> ) } 复制代码
可以看到,我们利用副作用钩子很好地实现了原本的生命周期方法。通常我们会利用组件的生命周期函数去获取数据,操作DOM等,而这些操作都被称作副作用(side effect)。这些副作用逻辑一般都比较复杂,也是bug频发的地段。 所以我们可以针对每一段逻辑单独使用一个Effect钩子,便于后期维护和调试。
在使用过程中, useEffect
方法需要传入两个参数,第一个参数是回调函数:这个回调函数会在每次组件渲染后执行,包括初始化渲染以及每次更新时。另一个参数,则是状态依赖项(数组形式),一旦检测到依赖项数据变动,组件会更新,并且回调函数都会被再次执行一遍,从而实现 componentDidUpdate
的功能。如果你传入一个空依赖,就能实现原来 componentDidMount
的效果,即只会执行一次。回调函数中如果返回的是闭包,这个返回的闭包函数将会在组件重新渲染前执行,所以你可以在这个位置做一些清理操作,从而实现 componentWillUnmount
的功能。
还有要注意的是 componentWillMount
和 componentWillUpdate
两个生命周期方法在新版本的React中已经不推荐使用了,具体原因可以查看这里。
至此,我们就学会如何利用 Effect
钩子在函数式组件中实现所有生命周期方法,从而管理我们的应用了。
自定义Hook
重用和抽象一直都是编程中要解决的问题。我们可以自己封装想要的Hook, 从而实现代码逻辑的重用和抽象。
封装自定义hook其实很简单,就是包装一个自定义函数,然后根据功能将其状态和对应的 effect
逻辑封装进去:
export const useFetch = (url, dependencies) => { const [isLoading, setIsLoading] = useState(false); const [response, setResponse] = useState(null); const [error, setError] = useState(null); useEffect(() => { setIsLoading(true); axios.get(url).then((res) => { setIsLoading(false); setResponse(res); }).catch((err) => { setIsLoading(false); setError(err); }); }, dependencies) return [isLoading, response, error]; } 复制代码
这里我们简单地封装了一个请求数据的Hook,使用方法跟其他Hook类似,直接调用就可以了:
export const Person = () => { const [isLoading, response, error] = useFetch("http://example.com/getPersonInfo", []); return ( <div> {isLoading ? <div>loading...</div> : ( error ? <div> There is an error happened. {error.message} </div> : <div> Welcome, {response.userName} </div> ) } </div> ) } 复制代码
注意事项
在使用Hooks的过程中,需要注意的两点是:
-
不要在循环,条件或嵌套函数中调用Hook,必须始终在React函数的顶层使用Hook。这是因为React需要利用调用顺序来正确更新相应的状态,以及调用相应的钩子函数。一旦在循环或条件分支语句中调用Hook,就容易导致调用顺序的不一致性,从而产生难以预料到的后果。
-
只能在React函数式组件或自定义Hook中使用Hook。
为了避免我们无意中破坏这些规则,你可以安装一个eslint插件:
npm install eslint-plugin-react-hooks --save-dev 复制代码
并在配置文件中使用它:
{ "plugins": [ // ... "react-hooks" ], "rules": { // ... "react-hooks/rules-of-hooks": "error" } } 复制代码
这样,一旦你违法上述这些原则,就会获得相应的提示。
最后,欢迎大家关注我的公众号,一起学习交流。
以上就是本文的全部内容,希望本文的内容对大家的学习或者工作能带来一定的帮助,也希望大家多多支持 码农网
猜你喜欢:- 那些你不得不知的抢购业务要点
- 字符串格式化你不得不知的那些事儿
- 前端开发不得不知的ES6十大新特性
- 升级Kubernetes 1.18前,你不得不知的9件事
- Google开发者大会:你不得不知的Tensorflow小技巧
- 作为DBA不得不知的5大管理MYSQL的开源免费利器
本站部分资源来源于网络,本站转载出于传递更多信息之目的,版权归原作者或者来源机构所有,如转载稿涉及版权问题,请联系我们。