在官网上,Hook简介的章节里,很安抚性地提到,Hook的提出不旨在替换Class创建组件,而是一种可选、100%向后兼容的特性。然而从使用效果的来说,Hook的应用,解决了render props 和高阶组件组织代码所带来晦涩难懂的窘况,让前端代码代码复用的粒度变得更低,代码可读性更高,研发效率自然也更高。我们团队的新代码,基本都在转向Hook,我也不例外,为了快速翻阅资料,整理以下核心信息,方便高效研发。
【关键点一】只能在函数最外层调用 Hook。不要在循环、条件判断或者子函数中调用。
可以阅读这个文章来深入理解:https://juejin.cn/post/6844903850567008270
或者原文 https://medium.com/@ryardley/react-hooks-not-magic-just-arrays-cd4f1857236e
【关键点二】不可以不学习Class Component而直接学习Hook,否则Hook将会显得非常难理解。
此外,只能在 React 的函数组件或者自定义Hook中调用 Hook。不要在其他 JavaScript 函数中调用。
提纲
- State Hook 直接对标Class Component中state的功能,更新state的值会触发Function Component的重新渲染
- Effect Hook 对标的是Class Component中的生命周期,处理componentDidMount、componentDidUpdate 和 componentWillUnmount三种生命周期中的附加行为,也就是副作用
- 自定义 Hook 对标高阶组件和 render props,在组件之间重用一些状态逻辑,也就是交互行为,可以释放非常多的前端人力
- Context Hook 不使用组件嵌套就可以订阅 React 的 Context
- Reducer Hook 通过 reducer 来管理组件本地的复杂 state,一种模仿Redux处理state的方案。
- Ref Hook 参考Class Component中的ref,用于访问子组件
State Hook
样例代码
代码语言:javascript复制import React,{ useState } from'react';
function Example(){
// 声明一个叫 “count” 的 state 变量。
const [count, setCount]=useState(0);
return(
<div>
<p>You clicked {count} times</p>
<buttononClick={()=>setCount(count 1)}> Click me</button>
</div>
);}
useState 唯一的参数就是初始 state
useState 会返回一对值:当前状态和一个让你更新它的函数;
类似 class 组件的 this.setState,但是它不会把新的 state 和旧的 state 进行合并
可以在一个组件中多次使用 State Hook
Effect Hook
样例代码
代码语言:javascript复制import React,{ useState, useEffect } from'react';
function Example(){
const [count, setCount] = useState(0);
useEffect(()=>{
// 组件渲染完毕之后,使用浏览器的 API 更新页面标题
// 相当于 componentDidMount 和 componentDidUpdate:
document.title =`You clicked ${count} times`;
const token = setInterval(function(){ alert("Hello"); }, 3000);
return ()=>{
// 清理附加到生命周期的行为,相当于 omponentWillUnmount
clearInterval(token);
}
});
return(<div>
<p>You clicked {count} times</p>
<buttononClick={()=>setCount(count 1)}> Click me </button>
</div>);}
跟 class 组件中的 componentDidMount、componentDidUpdate 和 omponentWillUnmount 具有相同的用途,只不过被合并成了一个 API
可以访问到组件的 props 和 state
副作用函数还可以通过返回一个函数来指定如何“清除”副作用。
跟 useState 一样,你可以在组件中多次使用 useEffect,这带来很清晰、粒度很细的属性和行为控制的能力
自定义 Hook
样例代码
代码语言:javascript复制import React, { useState, useEffect } from 'react';
// 一个自定义个Hook,用于侦听好友是否在线和取消侦听,在其他组件中,通过传入friendID既可以复用这里的行为
function useFriendStatus(friendID) {
const [isOnline, setIsOnline] = useState(null);
function handleStatusChange(status) {
setIsOnline(status.isOnline);
}
useEffect(() => {
// 启动侦听
ChatAPI.subscribeToFriendStatus(friendID, handleStatusChange);
return () => {
// 取消侦听
ChatAPI.unsubscribeFromFriendStatus(friendID, handleStatusChange);
};
});
return isOnline;
}
自定义 Hook 更像是一种约定而不是功能。如果函数的名字以 “use” 开头并调用其他 Hook,我们就说这是一个自定义 Hook。
创建涵盖各种场景的自定义 Hook,如表单处理、动画、订阅声明、计时器、webSocket的管理等。
Context Hook
样例代码
代码语言:javascript复制// Context 可以让我们无须明确地传遍每一个组件,就能将值深入传递进组件树。
// 为当前的 theme 创建一个 context(“light”为默认值)。
const ThemeContext = React.createContext('light');
class App extends React.Component {
render() {
// 使用一个 Provider 来将当前的 theme 传递给以下的组件树。
// 无论多深,任何组件都能读取这个值。
// 在这个例子中,我们将 “dark” 作为当前的值传递下去。
return (
<ThemeContext.Provider value="dark">
<Toolbar />
</ThemeContext.Provider>
);
}
}
// 中间的组件再也不必指明往下传递 theme 了。
function Toolbar() {
return (
<div>
<ThemedButton />
</div>
);
}
class ThemedButton extends React.Component {
// 指定 contextType 读取当前的 theme context。
// React 会往上找到最近的 theme Provider,然后使用它的值。
// 在这个例子中,当前的 theme 值为 “dark”。
static contextType = ThemeContext;
render() {
return <Button theme={this.context} />;
}
}
接收一个 context 对象(React.createContext 的返回值)并返回该 context 的当前值。当前的 context 值由上层组件中距离当前组件最近的 <MyContext.Provider> 的 value prop 决定
背景知识:Context 提供了一种在组件之间共享全局值的方式,而不必显式地通过组件树的逐层传递 props,是一种比Redux更老的共享数据的方案。
Reducer Hook
样例代码
代码语言:javascript复制// 计数器案例的 Reducer Hook改写方案
const initialState = {count: 0};
function reducer(state, action) {
switch (action.type) {
case 'increment':
return {count: state.count 1};
case 'decrement':
return {count: state.count - 1};
default:
throw new Error();
}
}
function Counter() {
const [state, dispatch] = useReducer(reducer, initialState);
return (
<>
Count: {state.count}
<button onClick={() => dispatch({type: 'decrement'})}>-</button>
<button onClick={() => dispatch({type: 'increment'})}> </button>
</>
);
}
useState 的替代方案。它接收一个形如 (state, action) => newState 的 reducer,并返回当前的 state 以及与其配套的 dispatch 方法。
如果 Reducer Hook 的返回值与当前 state 相同,React 将跳过子组件的渲染及副作用的执行
Ref Hook
样例代码
代码语言:javascript复制function TextInputWithFocusButton() {
const inputEl = useRef(null);
const onButtonClick = () => {
// `current` 指向已挂载到 DOM 上的文本输入元素
inputEl.current.focus();
};
return (
<>
<input ref={inputEl} type="text" />
<button onClick={onButtonClick}>Focus the input</button>
</>
);
}
useRef 返回一个可变的 ref 对象,其 .current 属性被初始化为传入的参数(initialValue)。返回的 ref 对象在组件的整个生命周期内保持不变。