React Hook(自定义 Hook)
前沿
想写下react hooks ,一些基本资料,当然也可以看官网,我只是整理下
react hooks 的官方基本介绍可以看 React 的文档:https://reactjs.org/docs/hooks-intro.html
一、State Hook
以一个简单的计数器举例,使用 useState
这个 Hook:
import { useState } from 'react'; function Example() { // Declare a new state variable, which we'll call "count" const [count, setCount] = useState(0); return ( <div> <p>You clicked {count} times</p> <button onClick={() => setCount(count + 1)}> Click me </button> </div> ); }
上面的 useState
实际上就是一个 Hook,我们在函数组件中调用它来向它添加一些本地的 state,而 React 在 re-render 之前会保留这个 state。useState
返回两个内容:
- 当前的 state 值
- 一个允许更新 state 值的 function
可以从事件处理程序或者其他位置调用返回的 function,和类中的 this.setState 比较累色,只不过不会将新的 state 和旧的 state 合并在一起。(React 给过一个使用 useState 和 this.state 的比较的示例https://reactjs.org/docs/hooks-state.html)
useState
的唯一参数是初始状态,在上面的例子中,初始 state 是 0,因为计数器从 0 开始。请注意,和 this.state 不同的是,这里的 state 不一定是对象,可以是随便需要的形式。而初始状态参数仅仅在第一次渲染的时候使用。
1、声明多个 state 变量
可以在单个组件中多次使用 state Hook:
function ExampleWithManyStates() { // Declare multiple state variables! const [age, setAge] = useState(42); const [fruit, setFruit] = useState('banana'); const [todos, setTodos] = useState([{ text: 'Learn Hooks' }]); // ... }
数组结构语法允许给 useState 的声明的 state 变量赋予不同的名称,这些名称并不是 useState API 的一部分。相反,React 假定如果多次调用 useState,则在每次渲染期间,以相同的顺序执行。
2、什么是 Hook
Hook 是允许从功能组件 挂钩
React 的 state 和生命周期方法等功能。Hook 在类的内部不起作用——允许在没有类的情况下使用 React。(React 不建议全部重写组件,只是建议如果感兴趣 Hook,可以在新的组件中使用)
React 提供了一些像 useState
这样的内置 Hook,还可以创建自己的 Hook 以便于重用不同组件之间的状态行为。
二、Effect Hook
我们已经可以在 React 组件中进行数据请求、subscriptions 或者是手动更改 DOM。
这些操作都是称为 side effects
,也就是副作用。因为它们会影响其他组件,并且在渲染过程中没有办法完成。
useEffect
增加了从功能组件执行副作用的功能。它与 React 类的 componentDidMount
、componentDidUpdate
和 componentWillUnmount
具有相同的效果,但是统一成了一个 API。可以在 Using the Effect Hool 文档中查找关于更多 useEffect
的内容。
例如,下面的组件在 React 更新 DOM 后设置文档标题:
import { useState, useEffect } from 'react'; function Example() { const [count, setCount] = useState(0); // Similar to componentDidMount and componentDidUpdate: useEffect(() => { // Update the document title using the browser API document.title = `You clicked ${count} times`; }); return ( <div> <p>You clicked {count} times</p> <button onClick={() => setCount(count + 1)}> Click me </button> </div> ); }
当调用useEffect
的时候,告诉 React 在刷新对 DOM 的更改后运行effect
副作用方法。因为 副作用方法 是在组件中声明的,因此可以访问 props 和 state。
默认情况下, React 在每次渲染后执行 effect 方法 —— 包括第一次渲染。想查看和生命周期方法的比较,也可以在 Using the Effect Hool 文档中找到。
同时 effect 副作用方法可以通过返回函数指定如何 “清理” 它们。例如,下面组件使用效果来订阅好友的在线 state,并通过取消订阅来实现清理的目的:
import { useState, useEffect } from 'react'; function FriendStatus(props) { const [isOnline, setIsOnline] = useState(null); function handleStatusChange(status) { setIsOnline(status.isOnline); } useEffect(() => { ChatAPI.subscribeToFriendStatus(props.friend.id, handleStatusChange); return () => { ChatAPI.unsubscribeFromFriendStatus(props.friend.id, handleStatusChange); }; }); if (isOnline === null) { return 'Loading...'; } return isOnline ? 'Online' : 'Offline'; }
在这个示例中,组件卸载或者是由于后面的 re-render 而重新执行副作用方法之前,React 会取消订阅 ChatAPI
。当然,如果 props 传过来的 id 是没有变化的,也可以通过某种方式来跳过订阅和取消订阅的行为:https://reactjs.org/docs/hooks-effect.html#tip-optimizing-performance-by-skipping-effects
和 useState
一样,可以在组件中使用多个 useEffect
:
function FriendStatusWithCounter(props) { const [count, setCount] = useState(0); useEffect(() => { document.title = `You clicked ${count} times`; }); const [isOnline, setIsOnline] = useState(null); useEffect(() => { ChatAPI.subscribeToFriendStatus(props.friend.id, handleStatusChange); return () => { ChatAPI.unsubscribeFromFriendStatus(props.friend.id, handleStatusChange); }; }); function handleStatusChange(status) { setIsOnline(status.isOnline); } // ...
Hooks 允许通过哪些部分相关(比如添加和删除订阅)来组织组件中的副作用,而不是基于生命周期方法进行强制拆分。
三、 Hooks 的规则
Hooks 是 JavaScript 方法, 但它们强加了两个额外的规则:
- 只能在顶层调用 Hooks,不能再循环、条件或者是嵌套方法中调用 Hooks。
- 仅在 React 功能组件中使用 Hooks。不能再常规的 JavaScript 方法中调用 Hook。
React 提供了一个 linter 插件 来自动执行这些规则。虽然这些规则可能会让开发者比较首先,但是对于 Hooks 的良好运行至关重要。
四、构建自己的 Hook
有时,可能会希望在组件之间重用一些 state 的逻辑,一般之前的做法都是通过高阶组件或者是 render props 来解决。自定义 Hook 能够做到这种需求,而不需要向 tree 中增加更多组件。
前面虽然介绍了 useState
和 useEffect
来订阅朋友的在线状态,如果还希望在另一个组件中重用订阅逻辑,首先需要将这个逻辑提取到一个名为 useFriendStatus
的自定义 Hook 中:
import { useState, useEffect } from 'react'; 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; }
这个方法参数是 friendID
,返回的是好友是否在线。
而现在可以在两个组件中直接使用这个 Hook:
function FriendStatus(props) { const isOnline = useFriendStatus(props.friend.id); if (isOnline === null) { return 'Loading...'; } return isOnline ? 'Online' : 'Offline'; } function FriendListItem(props) { const isOnline = useFriendStatus(props.friend.id); return ( <li style={{ color: isOnline ? 'green' : 'black' }}> {props.friend.name} </li> ); }
这些组件的状态是完全独立的,Hook 是重用有 state 逻辑的一种方式,而不是重用 state 本身。事实上,每次调用 Hook 都会有个完全隔离的状态。因此可以在一个组件中使用相同的自定义 Hook 两次。
自定义 Hook 更像是一种约定,而不是一种功能。如果函数的名字以 use
开头,并且调用了其他的 Hook,则就称其为一个自定义 Hook。useSomething
命名约定是为了 linter 插件在代码中查找错误。
五、其他的 Hooks
除了上面的 useState
和 useEffect
之外,还有一些其他不太常用的 Hook 也可能很有用。比如 useContext 允许订阅 React 上下文,不去引入嵌套。
function Example() { const locale = useContext(LocaleContext); const theme = useContext(ThemeContext); // ... }
而 useReducer 允许使用 reducer 管理复杂的组件 state:
function Todos() { const [todos, dispatch] = useReducer(todosReducer); // ...
就简答的介绍下HOOK吧,在数据请求的时候还是用到很多的