内容简介:在 React v16.7.0 alpha 版本里,提出了一个新的 Feature Proposal :Hooks ,对社区以及以后前端发展所带来的影响是巨大的。学习 Hooks 的知识需要对 React 生态有较深入的理解Hooks 是 React 内部组件中的一系列特殊函数,直观带来的改变是引入state、生命周期函数、或者其他 React 功能,无需使用 classes 编写组件(类语法带来的问题有很多),背后为前端带来更深入更普及的
在 React v16.7.0 alpha 版本里,提出了一个新的 Feature Proposal :Hooks ,对社区以及以后前端发展所带来的影响是巨大的。
学习 Hooks 的知识需要对 React 生态有较深入的理解
What is the Hooks ?
Hooks 是 React 内部组件中的一系列特殊函数,直观带来的改变是引入state、生命周期函数、或者其他 React 功能,无需使用 classes 编写组件(类语法带来的问题有很多),背后为前端带来更深入更普及的 functional programming
思想。
引入 Hooks 的动机
React 官方阐明了引入 Hooks 的动机,Hooks 出现前,我们编写 React 组件 会经常遇到的问题:
-
It’s hard to reuse stateful logic between components
-
React 没有提供官方方案去解决
组件之间共享复用有状态逻辑
,组件间逻辑的复用和数据传递就变得十分困难(必须一层一层往下传),所以我们使用render props
和higher-order components
来解决复用逻辑的同时引来了新的问题,一些无关 UI 的 wrapper 组件越来越多,嵌套组件越来越深,形成wrapper hell
,虽然 React devTools 有过滤器来帮助我们更容易地调试。 - 使用 Hooks 可以在不改变组件层次结构的情况下复用有状态逻辑 。可以利用 custom hooks,复用包含状态的逻辑,这些逻辑不再出现在组件树中,而是形成一个独立、可测试的单元,但仍然响应 React 在渲染之间的变化;社区之间分享 自定义hooks 更容易,hooks 就像插件一样。
-
React 没有提供官方方案去解决
-
Complex components become hard to understand
-
随着项目深入,我们逐渐会编写越来越复杂的逻辑在组件中,这导致了再生命周期函数内编写的逻辑非常臃肿,例如
添加监听器
,我们需要在componentDidMount
与componentWillUnmount
中分别编写添加与删除监听器的逻辑,而一般在componentDidMount
中,我们也会编写请求数据
的逻辑。各种功能不相关联的逻辑写在一起,而且相同功能的逻辑散落在不同函数内,这带来许多隐患以及调试上的困难 - 使用 Hooks 可以 将相关联的逻辑code由组件拆分出来成更简单直观的函数(例如订阅事件、请求数据)
-
随着项目深入,我们逐渐会编写越来越复杂的逻辑在组件中,这导致了再生命周期函数内编写的逻辑非常臃肿,例如
-
Classes confuse both people and machines
-
React 官方认为 JS 的 Class 语法的学习成本很高,使用类语法,要必须清楚
this
在 JS 的工作方式,例如我们需要 绑定事件处理程序 (以何种方式绑定这里不是重点,个人推荐箭头函数形式);另外一些重要实践上,使用 Class 语法也带来诸多问题,详细参阅 classes-confuse-both-people-and-machines ) - 使用 Hooks 可以 在无需编写 Class 语法的情况下 引入state、生命周期函数、或者其他 React 功能
-
React 官方认为 JS 的 Class 语法的学习成本很高,使用类语法,要必须清楚
实际上引入 Hooks 并不会给现有的代码带来问题
- 完全可选(将使用 Hooks 的选择权交给开发者)
- 向后兼容(不会有任何破坏性更改)
- 在可预见的未来内,不会从 React 中删除 类语法
- Hooks 并没有颠覆之前的 React 概念。相反,带来更直观的 API 实现相同的功能
编写 Hooks
目前主要的 Hooks :
- State hooks
- Effect hooks
- Custom hooks (自定义 hooks 用来复用包含状态的逻辑)
useState
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> ); } 复制代码
使用 state hooks
在 function components
中可以像上面代码这样,等同于Class语法的代码就不贴了。
值得一提的是,在 Hooks 出现之前,我们通常叫这样形式的组件为 stateless components
or stateless function components
,但现在,有了 Hooks ,我们可以在这类组件中使用 state,所以改称 function components
。
- useState 的参数是 我们需要定义的 state 名的初始值(不必像以前一样,state 必须为 Object,如果我们想要创建两个state,就调用两次 useState)
-
返回值是包含两个值的数组,两个值分别为
当前状态
和更新它的函数
。(这里我们使用array destructuring
的方式将值取出来。)
创建多个 state 就像这样
function ExampleWithManyStates() { // Declare multiple state variables! const [age, setAge] = useState(42); const [fruit, setFruit] = useState('banana'); const [todos, setTodos] = useState([{ text: 'Learn Hooks' }]); 复制代码
与 this.setState
不同,更新状态总是替换它而不是合并它(也解决了很多之前合并带来的问题)
Functional updates
如果新的 state 值是依赖上一个 state 值来计算的,我们可以给 setState
传递一个函数参数,这个函数的参数为上一个 state 的值,返回值是更新后的 state 值,例如:
function Counter({initialCount}) { const [count, setCount] = useState(initialCount); return ( <> Count: {count} <button onClick={() => setCount(0)}>Reset</button> <button onClick={() => setCount(prevCount => prevCount + 1)}>+</button> <button onClick={() => setCount(prevCount => prevCount - 1)}>-</button> </> ); } 复制代码
所以如果需要更新的 state 值为 Object,我们应该使用 object spread syntax
setState(prevState => { // Object.assign would also work return {...prevState, ...updatedValues}; }); 复制代码
延迟初始化 state
如果初始化的值是需要大量计算得到的结果,可以使用函数代替,此函数只会在初始化阶段执行
const [state, setState] = useState(() => { const initialState = someExpensiveComputation(props); return initialState; }); 复制代码
useEffect
Effect 其实就是 请求数据,操作DOM,以及订阅事件等一系列 副作用/效果
而 useEffect 则是 之前 componentDidMount
, componentDidUpdate
和 componentWillUnmount
的结合
React组件中有两种常见的 Effect:需要清理和不需要清理的 Effect
不需要清理的 Effect
import { useState, useEffect } from 'react'; function Example() { const [count, setCount] = useState(0); useEffect(() => { document.title = `You clicked ${count} times`; }); return ( <div> <p>You clicked {count} times</p> <button onClick={() => setCount(count + 1)}> Click me </button> </div> ); } 复制代码
- 将在每次渲染后执行 useEffect
- useEffect 写在 函数内部是为了直接访问到state值,利用了闭包的性质,不需要额外 API
需要清理的 Effect
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); // Specify how to clean up after this effect: return function cleanup() { ChatAPI.unsubscribeFromFriendStatus(props.friend.id, handleStatusChange); }; }); if (isOnline === null) { return 'Loading...'; } return isOnline ? 'Online' : 'Offline'; } 复制代码
需要单独的 API 来执行清理逻辑。因为添加和删除订阅的逻辑是相关的,useEffect 旨在将其保持在一起。 如果 useEffect 返回一个函数,React 将在清理时执行它
清理的时机是 当组件卸载时
,但,useEffect 会在每次渲染后运行而不仅仅是一次, 这就是 React 在下次执行 useEffect 之前还清除前一个 useEffect 的原因; Using the Effect Hook – React
如果要减少 useEffect 内并不是每次渲染都必要的逻辑,可以:
useEffect(() => { document.title = `You clicked ${count} times`; }, [count]); // Only re-run the effect if count changes 复制代码
React 会比较两次渲染的 count 值,如果一样,就会跳过这次 useEffect
Custom Hooks
我们可以封装在多个组件可重用的包含状态的逻辑,例如
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; } 复制代码
useFriendStatus
就是一个我们写好的复用逻辑函数,供其他组件调用。
多个组件使用 相同自定义Hooks,它们的状态和效果是 独立隔离的,仅仅是逻辑的复用。因为本质是 调用 Custom Hooks
是调用 useState
和 useEffect
,它们在一个组件调用很多次,彼此产生的状态也是完全独立的。
详细参见文档: Writing Custom Hooks – React
以上所述就是小编给大家介绍的《React Hooks (Proposal)》,希望对大家有所帮助,如果大家有任何疑问请给我留言,小编会及时回复大家的。在此也非常感谢大家对 码农网 的支持!
猜你喜欢:本站部分资源来源于网络,本站转载出于传递更多信息之目的,版权归原作者或者来源机构所有,如转载稿涉及版权问题,请联系我们。