一、Why
React 创建组件的方式,最常见的有两种,一个是类组件,例如 class Dashboard extends React.Component, 一种是纯函数组件, 例如 const Dashboard = () => {return <></>}, 而我们希望组件不要变成复杂的容器,最好只是数据的管道。所以组件的最佳写法应该是函数,而不是类。
但是,我们知道穿函数组件与类组件的区别是很大的,如:
纯函数组件
- 纯函数组件没有状态,只有接收的props
- 纯函数组件没有生命周期
- 纯函数组件没有this
- 只能是纯函数
类组件
- 遇到简单的页面,代码会显得很重。
- 每创建一个类组件,都要去继承一个React 实例。
import React from 'react'
class AddCount extends React.PureComponent {
constructor(props){
super(props)
this.state={
count: 0
}
}
addcount = () => {
let newCount = this.state.count
this.setState({
count: newCount +=1
})
}
render(){
return (
<div>
<p>{this.state.count}</p>
<button onClick={this.addcount}>count++</button>
</div>
)
}
}
export default AddCount
纯函数的这些特点,就注定纯函数组件只能做UI展示的功能,涉及到state,reducer我们就必须用类组件的形式,但是类组件又有上面明显的缺陷,所以为了解决以上的问题,React 设计出了React Hooks。所以React Hooks就是纯函数组件的升级版,可以完全不用class, 就能写出一个全功能的组件。
二、What
React Hooks的意思就是,组件尽量写成纯函数,如果需要跟函数外部环境发生的交互,就用Hooks 把外部代码“钩”进来,而React Hooks, 就是我们所说的“钩子”
三、How
React 为我们提供了一些常用的Hooks, 当然我们也可以根据自己的需求,自定义我们自己的Hook。我们可以看到react hooks 都是以use为前缀,所以react约定,钩子一律使用use作为前缀。
基础 Hook
- useState
- useEffect
- useContext
额外的 Hook
- useReducer
- useCallback
- useMemo
- useRef
- useImperativeHandle
- useLayoutEffect
- useDebugValue
四、Use
1. useState
const [state, setState] = useState(initialState);
返回一个 state,以及更新 state 的函数。
在初始渲染期间,返回的状态 (state) 与传入的第一个参数 (initialState) 值相同。
setState 函数用于更新 state。它接收一个新的 state 值并将组件的一次重新渲染加入队列。
function TestPage() {
const [count, setCount] = useState(0);
return (
<>
Count: {count}
<button onClick={() => setCount(0)}>Reset</button>
<button onClick={() => setCount(prevCount => prevCount - 1)}>-</button>
<button onClick={() => setCount(prevCount => prevCount + 1)}>+</button>
</>
)
}
2. useEffect
useEffect(didUpdate);
该 Hook 接收一个包含命令式、且可能有副作用代码的函数。
在函数组件主体内(这里指在 React 渲染阶段)改变 DOM、添加订阅、设置定时器、记录日志以及执行其他包含副作用的操作都是不被允许的,因为这可能会产生莫名其妙的 bug 并破坏 UI 的一致性。
使用 useEffect 完成副作用操作。赋值给 useEffect 的函数会在组件渲染到屏幕之后执行。你可以把 effect 看作从 React 的纯函数式世界通往命令式世界的逃生通道。
默认情况下,effect 将在每轮渲染结束后执行,但你可以选择让它 在只有某些值改变的时候 才执行。
const AsyncPage = ({ name }) => {
const [loading, setLoading] = useState(true)
const [person, setPerson] = useState({})
useEffect(() => {
setLoading(true)
setTimeout(() => {
setLoading(false)
setPerson({ name })
}, 2000)
}, [name])
return (
<>
{loading ? <p>Loading...</p> : <p>{person.name}</p>}
</>
)
}
3. useContext
const value = useContext(MyContext);
接收一个 context 对象(React.createContext 的返回值)并返回该 context 的当前值。当前的 context 值由上层组件中距离当前组件最近的 <MyContext.Provider> 的 value prop 决定。
const themes = {
light: {
foreground: "#000000",
background: 'red'
},
dark: {
foreground: "#ffffff",
background: 'green'
}
};
const ThemeContext = React.createContext(themes.light);
function TestPage() {
return (
<ThemeContext.Provider value={themes.dark}>
<Toolbar />
</ThemeContext.Provider>
);
}
function Toolbar(props) {
return (
<div>
<ThemedButton />
</div>
);
}
function ThemedButton() {
const theme = useContext(ThemeContext);
return (
<button style={{ background: theme.background, color: theme.foreground }}>
I am styled by theme context!
</button>
);
}
最终实现效果
4. Create ourself hooks
Link
通过自定义 Hook,可以将组件逻辑提取到可重用的函数中。
const usePerson = (name) => {
const [loading, setLoading] = useState(true)
const [person, setPerson] = useState({})
useEffect(() => {
setLoading(true)
setTimeout(() => {
setLoading(false)
setPerson({ name })
}, 2000)
}, [name])
return [loading, person]
}
const AsyncPage = ({ name }) => {
const [loading, person] = usePerson(name)
return (
<>
{loading ? <p>Loading...</p> : <p>{person.name}</p>}
</>
)
}
const PersonPage = () => {
const [state, setState] = useState('')
const changeName = (name) => {
setState(name)
}
return (
<>
<AsyncPage name={state} />
<button onClick={() => { changeName('名字1') }}>名字1</button>
<button onClick={() => { changeName('名字2') }}>名字2</button>
</>
)
}
五、Summarize
React 官网是这样说的
- 完全可选的, 你无需重写任何已有的代码就可以在一些组件中尝试Hook,但是如果你不想,你不必现在就去学习或是用Hook
- 100% 向后兼容,Hook不包含任何破坏性改动
- 现在可用,Hook已发布于v16.8.0, RN从0.59版本开始支持
- 没有计划从React中移除class
- Hook不会影响你对React的理解,恰恰相反,Hook为已知的React概念提供了更只记得API,如:props,state, context,ref以及生命周期
参考: https://www.jianshu.com/p/d600f749bb19