欢迎你们前往腾讯云+社区,获取更多腾讯海量技术实践干货哦~html
一句话总结 React Hooks 就是在 react 函数组件中,也可使用类组件(classes components)的 state 和 组件生命周期,而不须要在 mixin、 函数组件、HOC组件和 render props 之间来回切换,使得函数组件的功能更加实在,更加方便咱们在业务中实现业务逻辑代码的分离和组件的复用。git
本文将从如下几个方面介绍 hooksgithub
Hooks 在解决什么问题 Hooks 的 api 介绍 和如何使用 hooks Hooks 是怎么实现的api
React 一直在解决一个问题,如何实现分离业务逻辑代码,实现组件内部相关业务逻辑的复用。数组
通常状况下,咱们都是经过组件和自上而下传递的数据流将咱们页面上的大型UI组织成为独立的小型UI,实现组件的重用。可是咱们常常遇到很难侵入一个复杂的组件中实现重用,由于组件的逻辑是有状态的,没法提取到函数组件当中。这在处理动画和表单的时候,尤为常见,当咱们在组件中链接外部的数据源,而后但愿在组件中执行更多其余的操做的时候,咱们就会把组件搞得特别糟糕:机器学习
这时候,Hooks就派上用场了。 Hooks 容许咱们将组件内部的逻辑,组织成为一个可复用的隔离模块。ide
借用 @Sunil Pai 的两张图来讲明这个问题:函数
image.png性能
image.png
从 React Hooks 中体验出来的是 React 的哲学在组件内部的实现,之前咱们只在组件和组件直接体现 React 的哲学,就是清晰明确的数据流和组成形式。既能够复用组件内的逻辑,也不会出现 HOC 带来的层层嵌套,更加不会出现 Mixin 的弊端。
@dan_abramov 在会议上给咱们介绍了 hooks 的三个关键的api,分别是 State Hooks
、 Effect Hooks
、 Custom Hooks(自定义hooks)
useState 这个方法能够为咱们的函数组件带来 local state,它接收一个用于初始 state 的值,返回一对变量。 让函数组件拥有本身的组件。
首先若是咱们须要用 classes component 实现一个点击按钮 +1 组件应该怎么写呢?
import React from 'react'; class Example extends React.Component { constructor(props) { super(props); this.state = {count: 0}; this.clickBtn = this.clickBtn.bind(this); } clickBtn = () => { this.setState({ count: this.state.count + 1; }); } return ( <div> <p>You clicked {this.state.count} times</p> <button onClick={this.clickBtn}> Click me </button> </div> ); }
那使用 useState 是怎么样的呢? 能够看见很是清晰明了。
// 一个简单的点击计数 import { useState } from 'react'; function Example() { const [count, setCount] = useState(0); return ( <div> <p>You clicked {count} times</p> <button onClick={() => setCount(count + 1)}> Click me </button> </div> ); }
Effect Hooks 用于处理一些带有反作用的操做,下面经过监听窗口宽度的变化代码为例,说明 effect hooks 的使用fangfa
import { useState } from 'react'; function windowWidth() { const [width, setWithd] = useState(window.innerWidth); useEffect(() => { const handleResize = ()=>{ setWidth(window.innerWidth); } window.addEventListener('resize', handleResize); }); return ( <p> window width is {width}</p> ) }
useEffect 能够传入第二个操做来避免性能的损耗,若是第二个参数数组中的成员变量没有变化则会跳过这次改变。如何传入一个空数组 ,那么该 effect 只会在组件 mount 和 unmount 时期执行。
import { useState } from 'react'; function windowWidth() { const [width, setWithd] = useState(window.innerWidth); useEffect(() => { const handleResize = ()=>{ setWidth(window.innerWidth); } window.addEventListener('resize', handleResize); }, [width]); // width 没有变化则不处理 return ( <p> window width is {width}</p> ) }
useEffect 中还能够经过让函数返回一个函数来进行一些取消兼容之类的清理操做,好比取消订阅等
import { useState } from 'react'; function windowWidth() { const [width, setWithd] = useState(window.innerWidth); useEffect(() => { const handleResize = ()=>{ setWidth(window.innerWidth); } window.addEventListener('resize', handleResize); return () => { // 取消监听窗口的宽度变化 window.removeEventListener('resize'); } }); return ( <p> window width is {width}</p> ) }
如上所示,内置的 React Hooks 如 useState 和 useEffect 充当基本构建块。 咱们能够直接在组件中使用它们,或者咱们能够将它们组合到自定义Hook中,例如useWindowWidth。使用自定义Hooks感受就像使用React的内置API同样。
接着上面的监听窗口大小的代码,咱们接着讲自定义 hooks, 证实 react hooks 是怎么使到组件内的逻辑可复用的。
Talk is cheap, show me the code.
// 一个显示目前窗口大小的组件 function responsiveComponent(){ // custom hooks const width = useWindowWidth(); return ( <p>当前窗口的宽度是 {width}</p> ) }
上面的代码只有几行,很是清晰明了说明了他的做用就是监听当前窗口的变化,这就是Hooks的目标 - 使组件真正具备声明性,即便它们包含状态和反作用。
咱们来看看如何实现这个自定义Hook。咱们使用React本地状态来保持当前窗口宽度,并在窗口调整大小时使用反作用来设置该状态
import { useState, useEffect} from 'react'; // custom hooks to listen window width change function useWindowWidth(){ const [width, setWidth] = useState(window.innerWidth); useEffect(() => { const handleResize = ()=>{ setWidth(window.innerWidth); } window.addEventListener('resize', handleResize); }, [width]); // width 没有变化则不处理 return width; }
Hooks 是JavaScript函数,但它们强加了两个额外的规则:
这里有一些不经常使用的内置Hook。例如,useContext容许您订阅React上下文而不引入嵌套:
function Example() { const locale = useContext(LocaleContext); const theme = useContext(ThemeContext); // ... }
发现一个颇有趣的仓库,react-use, 包含了不少颇有趣的自定义hooks
如下内容翻译自 react-hooks-not-magic-just-arrays.
react hooks 其实只是一个数组,并非奇妙的魔法。
useState()
方法让咱们在这里经过一个例子来演示状态 hooks 的实现如何工做。
首先让咱们从一个组件开始:
function RenderFunctionComponent() { const [firstName, setFirstName] = useState("Rudi"); const [lastName, setLastName] = useState("Yardley"); return ( <Button onClick={() => setFirstName("Fred")}>Fred</Button> ); }
hooks API背后的想法是你可使用一个setter函数做为hook函数中的第二个数组项返回,而setter将控制由hook管理的状态。
让咱们了解这在React内部如何工做。 如下内容可在执行上下文中用于呈现特定组件。 这意味着此处存储的数据位于正在渲染的组件以外。 此状态不与其余组件共享,但它保留在能够随后渲染特定组件的范围内。
建立两个空数组:setters
和state
将光标设置为 0
image.png
初始化:两个空数组,Cursor为0
首次运行组件功能。
每次useState()调用,当在第一次运行时,将setter函数(绑定到光标位置)推送到setter数组,而后将某个状态推送到state数组。
image.png
第一次渲染:做为光标增量写入数组的项目。
每一个后续渲染都会重置光标,而且只从每一个数组中读取这些值。
image.png
后续渲染:从数组中读取的项目为光标增量
每一个setter都有一个对它的光标位置的引用,所以经过触发对任何setter的调用,它将改变状态数组中该位置的状态值。
image.png
Setters“记住”他们的索引并根据它设置内存。
这是一个演示实现的代码示例:
let state = []; let setters = []; let firstRun = true; let cursor = 0; function createSetter(cursor) { return function setterWithCursor(newVal) { state[cursor] = newVal; }; } // useState的伪代码实现 export function useState(initVal) { if (firstRun) { state.push(initVal); setters.push(createSetter(cursor)); firstRun = false; } const setter = setters[cursor]; const value = state[cursor]; cursor++; return [value, setter]; } // 模拟使用useState function RenderFunctionComponent() { const [firstName, setFirstName] = useState("Rudi"); // cursor: 0 const [lastName, setLastName] = useState("Yardley"); // cursor: 1 return ( <div> <Button onClick={() => setFirstName("Richard")}>Richard</Button> <Button onClick={() => setFirstName("Fred")}>Fred</Button> </div> ); } // 模拟Reacts渲染周期 function MyComponent() { cursor = 0; // 重置光标的位置 return <RenderFunctionComponent />; // render } console.log(state); // Pre-render: [] MyComponent(); console.log(state); // 首次渲染: ['Rudi', 'Yardley'] MyComponent(); console.log(state); // 后续渲染: ['Rudi', 'Yardley'] // 点击'Fred' 按钮 console.log(state); // 点击后: ['Fred', 'Yardley']
Hooks 还处于早期阶段,可是给咱们复用组件的逻辑提供了一个很好的思路,你们能够在 react-16.7.0-alpha.0 中体验。
此文已由做者受权腾讯云+社区发布,更多原文请点击
搜索关注公众号「云加社区」,第一时间获取技术干货,关注后回复1024 送你一份技术课程大礼包!
海量技术实践经验,尽在云加社区!