Hook是什么?
Hook 是 React 16.8 的新增特性。它可以让你在不编写 class 的情况下使用 state 以及其他的 React 特性
Hook 是一些可以让你在函数组件里“钩入” React state 及生命周期等特性的函数。
总结:Hook 可以让开发者不使用复杂的class,在函数中即可使用更多React的特性,让React的开发体验更为注重于函数式开发;
Hook 产生的动机
在组件之间复用状态逻辑很难
React 没有提供将可复用性行为“附加”到组件的途径(例如,把组件连接到 store)。如果你使用过 React 一段时间,你也许会熟悉一些解决此类问题的方案,比如 render props 和 高阶组件。但是这类方案需要重新组织你的组件结构,这可能会很麻烦,使你的代码难以理解。如果你在 React DevTools 中观察过 React 应用,你会发现由 providers,consumers,高阶组件,render props 等其他抽象层组成的组件会形成“嵌套地狱”。
- Hook 使你在无需修改组件结构的情况下复用状态逻辑
复杂组件变得难以理解
组件起初很简单,但是逐渐会被状态逻辑和副作用充斥。每个生命周期常常包含一些不相关的逻辑。例如,组件常常在 componentDidMount 和 componentDidUpdate 中获取数据。但是,同一个 componentDidMount 中可能也包含很多其它的逻辑,如设置事件监听,而之后需在 componentWillUnmount 中清除。相互关联且需要对照修改的代码被进行了拆分,而完全不相关的代码却在同一个方法中组合在一起。如此很容易产生 bug,并且导致逻辑不一致。
- Hook 将组件中相互关联的部分拆分成更小的函数(比如设置订阅或请求数据)
难以理解的 class
你必须去理解 JavaScript 中 this 的工作方式,这与其他语言存在巨大差异。还不能忘记绑定事件处理器。没有稳定的语法提案,这些代码非常冗余。大家可以很好地理解 props,state 和自顶向下的数据流,但对 class 却一筹莫展。即便在有经验的 React 开发者之间,对于函数组件与 class 组件的差异也存在分歧,甚至还要区分两种组件的使用场景
- Hook 使你在非 class 的情况下可以使用更多的 React 特性
Hook 使用规则
只在最顶层使用 Hook
不要在循环,条件或嵌套函数中调用 Hook, 确保总是在你的 React 函数的最顶层调用他们。遵守这条规则,你就能确保 Hook 在每一次渲染中都按照同样的顺序被调用。这让 React 能够在多次的 useState 和 useEffect 调用之间保持 hook 状态的正确。
原因 React 靠的是 Hook 调用的顺序来确定哪个 state 对应哪个 useState
因此当加了循环,条件或嵌套则会导致state 和 useState不一一对应;
代码如下(示例):
// ------------
// 首次渲染
// ------------
useState('Mary') // 1. 使用 'Mary' 初始化变量名为 name 的 state
useEffect(persistForm) // 2. 添加 effect 以保存 form 操作
useState('Poppins') // 3. 使用 'Poppins' 初始化变量名为 surname 的 state
useEffect(updateTitle) // 4. 添加 effect 以更新标题
// -------------
// 二次渲染
// -------------
useState('Mary') // 1. 读取变量名为 name 的 state(参数被忽略)
useEffect(persistForm) // 2. 替换保存 form 的 effect
useState('Poppins') // 3. 读取变量名为 surname 的 state(参数被忽略)
useEffect(updateTitle) // 4. 替换更新标题的 effect
// ...
只在 React 函数中调用 Hook
- 不要在普通的 JavaScript 函数中调用 Hook,你可以:
- 在 React 的函数组件中调用 Hook
- 在自定义 Hook 中调用其他 Hook
Hooks基础hook 及使用
1.useState
hook 推出之前,函数只能作为无状态组件,要在组件中使用state,必须要使用类;
而useState Hook就是为了 用于在函数组件中使用state.
代码如下(示例):
const Demo: React.FC = () => {
const [count, setCount] = useState(1);
return (
<div>
<p>{count}</p>
<button onClick={() => setCount(count + 1)}>加1</button>
</div>
)
}
2.useEffect
Effect Hook 可以让你在函数组件中执行副作用操作
当useEffect 返回一个函数即代表React 会在组件卸载的时候执行的清除操作(相当于 componentWillUnmount)
useEffect 第二个参数,即当传入的参数值发生变化才执行这个Effect(副作用操作),当不传入时代表Effect 在首次渲染及每次更新后都会执行,当传入空数组[] 代表首次渲染执行一次(相当于componentDidMount)
代码如下(示例):
const Demo: React.FC = () => {
const [count, setCount] = useState(1);
const [time, setTime] = useState(new Date());
useEffect(() => {
// componentDidMount
console.log('componentDidMount');
return () => { console.log('unmount') } // componentWillUnmount
}, []);
// 和componentDidUpdate类似,区别是Mount阶段,hooks也会执行一次
useEffect(() => {
console.log(time)
console.log('componentDidUpdate');
});
// count改变才会执行
useEffect(() => {
console.log('count change');
}, [count]);
return (
<div>
<p onClick={() => setTime(new Date())}>{count}</p>
<button onClick={() => setCount(count + 1)}>加1</button>
</div>
)
}
3.useContext
const value = useContext(MyContext);
使用方式如上(示例):
接收一个 context 对象(React.createContext 的返回值)并返回该 context 的当前值。当前的 context 值由上层组件中距离当前组件最近的 <MyContext.Provider> 的 value prop 决定
当组件上层最近的 <MyContext.Provider> 更新时,该 Hook 会触发重渲染,并使用最新传递给 MyContext provider 的 context value 值。
调用了 useContext 的组件总会在 context 值变化时重新渲染
总结:该Hook 可以用于多层嵌套组件共享状态, 在context改变时触发使用useContext的组件重新渲染;
代码如下(示例):
const ShowContext = React.createContext(false); // 创建context对象
export default function Page() {
const [show, setShow] = useState(false);
return (
<div className={styles.title}>
<Demo />
<ShowContext.Provider value={show}>
<ContextDemo />
</ShowContext.Provider>
<button style={{ marginTop: '10px' }} onClick={() => setShow(!show)}>{show ? '隐藏' : '展示'}</button>
</div>
);
}
const ContextDemo: React.FC = () => {
const show = useContext(ShowContext);
return (
<div>
<div style={{ display: show ? '' : 'none' }}>hello world</div>
</div>
)
}