Hooks介绍
之前没有用hooks写react的时候,用class类组件,state在构造函数定义,然后是钩子函数。
在这里,发现用的是函数组件,useState来定义state,useEffect来初始获取接口数据,比如查询列表等等。使用hooks的目的是让函数组件具备class组件的能力。
- React 一直都提倡使用函数组件,但是有时候需要使用 state 或者其他一些功能时,只能使用类组件,因为函数组件没有实例,没有生命周期函数,只有类组件才有。
- Hooks 是 React 16.8 新增的特性,React Hooks 就是让你不必写class组件就可以用state和其他的React特性;
- 如果你在编写函数组件并意识到需要向其添加一些 state,以前的做法是必须将其它转化为 class。现在你可以直接在现有的函数组件中使用 Hooks
- 凡是 use 开头的 React API 都是 Hooks
这里useState比原来的setstate感觉方便一点。在这里,没有super(props),没有this,没有生命周期。hooks其实就是对原有React 的 API 进行了封装,暴露比较方便使用的钩子。
常用hook
useState 初始化和设置状态 。会返回一个数组:一个 state,一个更新 state 的函数。
const [isEdit, setEdit] = useState(false);
- 在初始化渲染期间,返回的状态 (state) 与传入的第一个参数 (initialState) 值相同
- 你可以在事件处理函数中或其他一些地方调用这个函数。它类似 class 组件的 this.setState,但是它不会把新的 state 和旧的 state 进行合并,而是直接替换
useEffect componentDidMount,componentDidUpdate和componentWillUnmount和结合体,所以可以监听useState定义值的变化 监听变化 。会在第一次渲染之后和每次更新之后都会执行。
useEffect(() => {
getList();
},
[fresh]
);
fresh 改变,就会调用getList()。
import React, { useState, useEffect } from 'react'; import axios from 'axios'; function App() { const [data, setData] = useState({ hits: [] }); useEffect(async () => { const result = await axios( 'http://localhost/api/v1/search?query=redux', ); setData(result.data); }); return ( <ul> {data.hits.map(item => ( <li key={item.objectID}> <a href={item.url}>{item.title}</a> </li> ))} </ul> ); } export default App;
在useEffect中,不仅会请求后端的数据,还会通过调用setData来更新本地的状态,这样会触发view的更新。
useEffect在组件mount时执行,但也会在组件更新时执行。因为我们在每次请求数据之后都会设置本地的状态,所以组件会更新,因此useEffect会再次执行,因此出现了无限循环的情况。我们只想在组件mount时请求数据。我们可以传递一个空数组作为useEffect的第二个参数,这样就能避免在组件更新执行useEffect,只会在组件mount时执行。就是图一里面的使用方法。
useCallback 记忆作用,共有两个参数,第一个参数为一个匿名函数,就是我们想要创建的函数体。第二参数为一个数组,里面的每一项是用来判断是否需要重新创建函数体的变量,如果传入的变量值保持不变,返回记忆结果。如果任何一项改变,则返回新的结果。
const submit = useCallback(() => { validateFields((err, values) => { if (err) { console.log(err); return message.info("请完整填写表单"); } else { onOk({ vmData, userId: values.userId }); } }); }, []);
useMemo 和useCallback就是解决性能问题的杀手锏。
useCallback和useMemo的参数跟useEffect一致,他们之间最大的区别有是useEffect会用于处理副作用,而前两个hooks不能。
useMemo和useCallback都会在组件第一次渲染的时候执行,之后会在其依赖的变量发生改变时再次执行;并且这两个hooks都返回缓存的值,useMemo返回缓存的变量,useCallback返回缓存的函数。
他们的作用,就是类似生命周期中的shouldComponentUpdate,当变量改变,去判断是否重新渲染。
知道useCallback有什么样的特点,那有什么作用呢?
使用场景是:有一个父组件,其中包含子组件,子组件接收一个函数作为props;通常而言,如果父组件更新了,子组件也会执行更新;但是大多数场景下,更新是没有必要的,我们可以借助useCallback来返回函数,然后把这个函数作为props传递给子组件;这样,子组件就能避免不必要的更新。
不仅是上面的例子,所有依赖本地状态或props来创建函数,需要使用到缓存函数的地方,都是useCallback的应用场景。
useContext 定义一个全局的对象,类似 context
useReducer 可以增强函数提供类似 Redux 的功能
useMemo 作用和传入参数与 useCallback 一致,useCallback返回函数,useDemo 返回值
useRef 获取 ref 属性对应的 dom
前面三个用的比较多。另外,useEffect、useMemo、useCallback都是自带闭包的。也就是说,每一次组件的渲染,其都会捕获当前组件函数上下文中的状态(state, props),所以每一次这三种hooks的执行,反映的也都是当前的状态,你无法使用它们来捕获上一次的状态。对于这种情况,我们应该使用ref来访问。
注意
- 只能在顶层调用 Hook,不要在循环、条件或嵌套函数中调用 Hook。
- 仅从 React 函数式组件中调用 Hook。不要从常规 JavaScript 函数调用 Hook。(还有另一个有效的地方来调用 Hook,即你的自定义 Hook。)
举个栗子
import React, { Fragment, useState, useEffect } from 'react'; import axios from 'axios'; function App() { const [data, setData] = useState({ hits: [] }); const [query, setQuery] = useState('redux'); const [url, setUrl] = useState( 'http://localhost/api/v1/search?query=redux', ); const [isLoading, setIsLoading] = useState(false); const [isError, setIsError] = useState(false); useEffect(() => { const fetchData = async () => { setIsError(false); setIsLoading(true); try { const result = await axios(url); setData(result.data); } catch (error) { setIsError(true); } setIsLoading(false); }; fetchData(); }, [url]); return ( <Fragment> <input type="text" value={query} onChange={event => setQuery(event.target.value)} /> <button type="button" onClick={() => setUrl(`http://localhost/api/v1/search?query=${query}`) } > Search </button> {isError && <div>Something went wrong ...</div>} {isLoading ? ( <div>Loading ...</div> ) : ( <ul> {data.hits.map(item => ( <li key={item.objectID}> <a href={item.url}>{item.title}</a> </li> ))} </ul> )} </Fragment> ); } export default App;
在useEffect中,请求数据前将loading置为true,在请求完成后,将loading置为false。我们可以看到useEffect的依赖数据中并没有添加loading,这是因为,我们不需要再loading变更时重新调用useEffect。请记住:只有某个变量更新后,需要重新执行useEffect的情况,才需要将该变量添加到useEffect的依赖数组中。
loading处理完成后,还需要处理错误,这里的逻辑是一样的,使用useState来创建一个新的state,然后在useEffect中特定的位置来更新这个state。由于我们使用了async/await,可以使用一个大大的try-catch。
每次useEffect执行时,将会重置error;在出现错误的时候,将error置为true;在正常请求完成后,将error置为false。
阿里的umi HOOKs
https://hooks.umijs.org/zh-CN/hooks/life-cycle/use-mount
这个就感觉还蛮厉害的,但是得慎用。
用不好,一堆副作用,维护很麻烦!