React函数式组件核心——Hooks
Hooks本质上就是一类特殊的函数,它们可以为你的函数型组件(function component)注入一些特殊的功能。
useState
未使用useState的类式组件的案例
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>
);
}
}
我们使用hooks后,就变得十分简单
import { useState } from 'react';
function Example() {
const [count, setCount] = useState(0);
return (
<div>
<p>You clicked {count} times</p>
<button onClick={() => setCount(count + 1)}>
Click me
</button>
</div>
);
}
可以看到,Example
变成了一个函数,但这个函数却有自己的状态(count),同时它还可以更新自己的状态(setCount)。
useEffect
在类式组件中,提供了一些声明周期钩子给我们使用,我们可以在组件的特殊时期执行特定的事情,例如 componentDidMount ,能够在组件挂载完成后执行一些东西
在函数式组件中也可以实现,它采用的是 effectHook ,它的语法更加的简单,同时融合了 componentDidUpdata 生命周期,极大的方便了我们的开发
React.useEffect(() => { console.log('被调用了');})
由于函数的特性,我们可以在函数中随意的编写函数,这里我们调用了 useEffect 函数,这个函数有多个功能
当我们像上面代码那样使用时,它相当于 componentDidUpdata 和 componentDidMount 一同使用,也就是在组件挂载和组件更新的时候都会调用这个函数
它还可以接收第二个参数,这个参数表示它要监测的数据,也就是他要监视哪个数据的变化
当我们不需要监听任何状态变化的时候,我们可以就传递一个空数组,这样它就能当作componentMidMount
来使用
React.useEffect(() => { console.log('被调用了');}, [])
这样我们只有在组件第一次挂载的时候触发
当然当页面中有多个数据源时,我们也可以选择个别的数据进行监测以达到我们想要的效果
React.useEffect(() => { console.log('被调用了');}, [count])
这样,我们就只监视 count 数据的变化
当我们想要在卸载一个组件之前进行一些清除定时器的操作,在类式组件中,我们会调用生命周期钩子 componentDidUnmount 来实现,在函数式组件中,我们的写法更为简单,我们直接在 useEffect 的第一个参数的返回值中实现即可
也就是说,第一个参数的函数体相当于 componentDidMount 返回体相当于 componentDidUnmount ,这样我们就能实现在组件即将被卸载时输出一些东西了
实现卸载
function unmount() { ReactDOM.unmountComponentAtNode(document.getElementById("root"))}
卸载前输出
React.useEffect(() => { console.log('被调用了'); return () => { console.log('我要被卸载了'); }}, [count])
因此
useEffect
相当于三个生命周期钩子,componentDidMount
、componentDidUpdata
、componentDidUnmount
useLayoutEffect
和 useEffect
很类似
它的作用是:在 DOM 更新完成之后执行某个操作
注意:
- 有 DOM 操作的副作用 hooks
- 在 DOM 更新之后执行
执行时机在
useEffect
之前,其他都和useEffect
都相同
useEffect
执行时机在 render 之后
useLayoutEffect
执行时机在 DOM 更新之后
useMemo
作用:让组件中的函数跟随状态更新
注意:优化函数组件中的功能函数
为了避免由于其他状态更新导致的当前函数的被迫执行
第一个参数接收一个函数,第二个参数为数组的依赖列表,返回一个值
const getDoubleNum = useMemo(() => {
console.log('ddd')
return 2 * num
}, [num])
useCallback
作用:跟随状态更新执行
注意:
- 只有依赖项改变时才执行
- useMemo( () => fn, deps) 相当于 useCallback(fn, deps)
不同点:
- useCallback 返回的是一个函数,不再是值
- useCallback 缓存的是一个函数,useMemo 缓存的是一个值,如果依赖不更新,返回的永远是缓存的那个函数
- 给子组件中传递 props 的时候,如果当前组件不更新,不会触发子组件的重新渲染
useContext
作用:带着子组件渲染
与类似组件中使用的context类似
注意:
- 上层数据发生改变,肯定会触发重新渲染
- 我们需要引入 useContext 和 createContext 两个内容
- 通过 createContext 创建一个 Context 句柄
- 通过 Provider 确定数据共享范围
- 通过 value 来分发数据
- 在子组件中,通过 useContext 来获取数据
import React, { useContext, createContext } from 'react'
const Context = createContext(null)
export default function Hook() {
const [num, setNum] = React.useState(1)
return (
<h1>
这是一个函数组件 - {num}
// 确定范围
<Context.Provider value={num}>
<Item1 num={num} />
<Item2 num={num} />
</Context.Provider>
</h1>
)
}
function Item1() {
const num = useContext(Context)
return <div>子组件1 {num}</div>
}
function Item2() {
const num = useContext(Context)
return <div>子组件2 {num}</div>
}
useReducer
作用:去其他地方借资源
注意:函数组件的 Redux 的操作
- 创建数据仓库
store
和管理者reducer
- 通过
useReducer(store,dispatch)
来获取state
和dispatch
const store = {
num: 10
}
const reducer = (state, action) => {
switch (action.type) {
case "":
return
default:
return
}
}
const [state, dispatch] = useReducer(reducer, store)
通过 dispatch
去派发 action
自定义Hooks
放在 utils
文件夹中,以 use
开头命名
例如:模拟数据请求的 Hooks
import React, { useState, useEffect } from "react";
function useLoadData() {
const [num, setNum] = useState(1);
useEffect(() => {
setTimeout(() => {
setNum(2);
}, 1000);
}, []);
return [num, setNum];
}
export default useLoadData;
减少代码耦合
我们希望 reducer 能让每个组件来使用,我们自己写一个 hooks
自定义一个自己的 LocalReducer
import React, { useReducer } from "react";
const store = { num: 1210 };
const reducer = (state, action) => {
switch (action.type) {
case "num":
return { ...state, num: action.num };
default:
return { ...state };
}
};
function useLocalReducer() {
const [state, dispatch] = useReducer(reducer, store);
return [state, dispatch];
}
export default useLocalReducer;
自定义Hooks步骤:
- 引入 react 和自己需要的 hook
- 创建自己的hook函数
- 返回一个数组,数组中第一个内容是数据,第二个是修改数据的函数
- 暴露自定义 hook 函数出去
- 引入自己的业务组件