序言

---最后有招聘信息哦~

React是一个库,它不是一个框架。用于构建用户界面的Javascript库。这里大家需要认识这一点。react的核心在于它仅仅是考虑了如何将dom节点更快更好更合适的渲染到浏览器中。它本身提供的涉及框架的理念是不多的。class组件是如此,hooks组件也是如此。

ClassComponent

我们先回顾一下,这是一个react的class组件:

class HelloMessage extends React.Component {
constructor (props) {
super(props)
this.state = {
num: 1
}
}
componentDidMount () {
alert('mounted!')
}
addNum () {
this.setState({
num: this.state.num + 1;
})
}
render() {
return (
<div>
Hello {this.props.name}
</div>
);
}
} ReactDOM.render(
<HelloMessage name="Taylor" />,
document.getElementById('hello-example')
);

它包含了:

  • props
  • state
  • setState
  • render
  • 生命周期

再来看看class组件中比较特殊的,具有代表意义的东西:

1、setState

通过对象更新的方式更新组件状态,react通过diff算法自动更新dom

2、Ref

一个可以用来访问dom对象的东西

3、this

classComponent中通过this访问各种数据/方法

4、生命周期

class组件存在的问题

* 逻辑分散
* 数据分散
* 组件拆分繁琐,state树往往较大

我们构建React组件的方式与组件的生命周期是耦合的。这一鸿沟顺理成章的迫使整个组件中散布着相关的逻辑。在下面的示例中,我们可以清楚地了解到这一点。有三个的方法componentDidMount、componentDidUpdate和updateRepos来完成相同的任务——使repos与任何props.id同步。

componentDidMount () {
this.updateRepos(this.props.id)
}
componentDidUpdate (prevProps) {
if (prevProps.id !== this.props.id) {
this.updateRepos(this.props.id)
}
}
updateRepos = (id) => {
this.setState({ loading: true })
fetchRepos(id)
.then((repos) => this.setState({
repos,
loading: false
}))
}

为了解决这个问题(它是一系列问题的其中之一,这里只简单举例),react创造了一个全新的方式来解决,那就是hooks。

FunctionalComponent

hooks的目的是让大家在一定程度上 “使用函数的想法去开发组件” 。
纯函数组件、高阶组件
先粗浅理解函数式

主要思想是把运算过程尽量写成一系列嵌套的函数调用。举例来说,现在有这样一个数学表达式:

(1 + 2) * 3 - 4

传统的过程式编程,可能这样写:

var a = 1 + 2;

var b = a * 3;

var c = b - 4;

函数式编程要求使用函数,我们可以把运算过程定义为不同的函数,然后写成下面这样:

var result = subtract(multiply(add(1,2), 3), 4);

函数式的特点

  1. 函数是"第一等公民"

  2. 只用"表达式",不用"语句"

"表达式"(expression)是一个单纯的运算过程,总是有返回值;"语句"(statement)是执行某种操作,没有返回值。函数式编程要求,只使用表达式,不使用语句。也就是说,每一步都是单纯的运算,而且都有返回值。

原因是函数式编程的开发动机,一开始就是为了处理运算(computation),不考虑系统的读写(I/O)。"语句"属于对系统的读写操作,所以就被排斥在外。

当然,实际应用中,不做I/O是不可能的。因此,编程过程中,函数式编程只要求把I/O限制到最小,不要有不必要的读写行为,保持计算过程的单纯性。

纯函数组件
FunctionComponent:

满足“不修改状态”, “引用透明” ,“组件一等公民”, “没有副作用”, “表达式

高阶组件
higherOrderComponent(高阶组件):

传入一个组件,返回另一个包装组件,向其注入数据、逻辑,达到拆分组件的目的

import * as React from 'react';

export default function () {
return (
ComponentA({ Com: ComponentB })
)
} function ComponentA (
{ Com }: { Com: React.FunctionComponent<any> }
) {
return (
<div>
<Com style={{ color: 'red' }} />
</div>
)
} function ComponentB ({ style }: {
style: any
}) {
return (
<div style={style}>hello high order component</div>
)
}

Hooks与实践

Hooks你可以理解为具备class组件功能的函数式组件——至少它的理想是函数式

HooksAPI-今天的重点不在这里,这里不去细细讲它

1、useState

2、useReducer

3、useEffect

4、useLayoutEffect

5、useMemo

6、useCallback

7、useContext

8、useRef

Hooks基本规则
1、只在最顶层使用 Hook(why? 后文提到)

不要在循环,条件或嵌套函数中调用 Hook, 确保总是在你的 React 函数的最顶层调用他们。遵守这条规则,你就能确保 Hook 在每一次渲染中都按照同样的顺序被调用。这让 React 能够在多次的 useState 和 useEffect 调用之间保持 hook 状态的正确。(如果你对此感到好奇,在下面会有更深入的解释。)

2、只在 React 函数中调用 Hook
3、严格控制函数的行数(相信我,超过500行的函数,任何人都会头秃)
hooks生命周期对应

export default function HooksComponent () {
const [ date, setDate ] = React.useState(new Date());
const div = React.useRef(null); const tick = React.useCallback(() => {
setDate(new Date());
}, []) const color = React.useCallback(() => {
if (div.current) {
if (div.current.style.cssText.indexOf('red') > -1) {
div.current.style.cssText = 'background: green;'
} else {
div.current.style.cssText = 'background: red;'
}
}
}, [ div ]); const timerID = React.useMemo(() => {
console.log('==组件是否更新(概念有区别)==')
return setInterval(
() => tick(),
1000
);
}, [ div ]) React.useEffect(() => {
console.log('==组件加载完成==')
return () => {
console.log('==组件将要销毁==')
clearInterval(timerID);
}
}, []) React.useEffect(() => {
console.log('==组件更新完成==')
color();
}, [ date ]) console.log('==组件将要加载==')
console.log('==组件获取新的props==')
console.log('==组件将要更新==') return (
<DemoComponent />
)
}

Hooks原理demo实现

实现文件:

import * as React from 'react';

type State = {
[key: string]: any
} export type Reducer = (state: State, action: string) => State; export type UseReducer = typeof demoReducer; type Queue<T> = {
root: Queue<T> | null,
next: Queue<T> | null,
prev: Queue<T> | null,
value: T,
index: number
} const memo: Queue<State>[] = []; let index = 0; function demoReducer (reducer: Reducer, initalState?: State, update?: () => void) : any[] {
let has = !!memo[index];
if (!memo[index]) {
if (index === 0) {
memo[0] = GeQueue<State>(initalState, index);
memo[0].root = memo[0];
} else {
memo[index] = GeQueue<State>(initalState, index);
memo[index].root = memo[0];
memo[index].prev = memo[index - 1];
memo[index-1].next = memo[index];
}
index = index + 1;
}
let state;
if (has) {
state = memo[index].value;
index = index + 1;
} else {
state = memo[index - 1].value;
}
let bindex = index;
return [
state,
(action: string) => {
memo[bindex - 1].value = reducer(memo[bindex - 1].value, action)
update();
}
]
} function GeQueue<T>(value: T, index: number): Queue<T> {
return {
root: null,
prev: null,
next: null,
value,
index
}
} export function DemoHoc ({ Com }: any) {
const [ u, setU ] = React.useState(1)
return (
<div>
<Com useReducer={(reducer: Reducer, initalState?: State) => {
return demoReducer(reducer, initalState, () => {
index = 0;
setU(u + 1);
})
}} />
</div>
)
}

使用文件:

import * as React from 'react';
import { DemoHoc, UseReducer } from './useReducer/useReducerDemo'
import { Button } from 'antd'; export default function HookDemo () {
return (
<DemoHoc Com={DemoComponent} />
)
} const initialState = {count: 0}; function reducer(state: any, action: any) {
switch (action.type) {
case 'increment':
return {count: state.count + 1};
case 'decrement':
return {count: state.count - 1};
default:
throw new Error();
}
} function DemoComponent({ useReducer }: {
useReducer: UseReducer
}) {
const [ state, dispatch ] = useReducer(reducer, initialState);
const [ state2, dispatch2 ] = useReducer(reducer, {...initialState}); return (
<div>
Count: {state.count}
<Button onClick={() => dispatch({type: 'decrement'})}>-</Button>
<Button onClick={() => dispatch({type: 'increment'})}>+</Button>
<div></div>
Count2: {state2.count}
<Button onClick={() => dispatch2({type: 'decrement'})}>-</Button>
<Button onClick={() => dispatch2({type: 'increment'})}>+</Button>
</div>
)
}

原理:
我们先看实现文件的这个函数(不了解ts的同学,可以忽略ts的理解):

const memo: Queue<State>[] = [];

let index = 0;
function demoReducer (reducer: Reducer, initalState?: State, update?: () => void) : any[] {
let has = !!memo[index];
if (!memo[index]) {
if (index === 0) {
memo[0] = GeQueue<State>(initalState, index);
memo[0].root = memo[0];
} else {
memo[index] = GeQueue<State>(initalState, index);
memo[index].root = memo[0];
memo[index].prev = memo[index - 1];
memo[index-1].next = memo[index];
}
index = index + 1;
}
let state;
if (has) {
state = memo[index].value;
index = index + 1;
} else {
state = memo[index - 1].value;
}
let bindex = index;
return [
state,
(action: string) => {
memo[bindex - 1].value = reducer(memo[bindex - 1].value, action)
update();
}
]
}
  • 最开始,我们用了一个memo对象(它是一个队列)这里简单的定义成数组。 然后又又一个index,记录数字关系
  • 然后重点来了
let has = !!memo[index];
if (!memo[index]) {
if (index === 0) {
memo[0] = GeQueue<State>(initalState, index);
memo[0].root = memo[0];
} else {
memo[index] = GeQueue<State>(initalState, index);
memo[index].root = memo[0];
memo[index].prev = memo[index - 1];
memo[index-1].next = memo[index];
}
index = index + 1;
}
let state;
if (has) {
state = memo[index].value;
index = index + 1;
} else {
state = memo[index - 1].value;
}
let bindex = index;

我们调用useReducer的时候,从memo中尝试获取index的值,若存在的话,将state赋值为当前memo里的值并返回

  • 最后我们提供了一个触发更新的函数

然后-我们再看看使用的代码:

const initialState = {count: 0};

function reducer(state: any, action: any) {
switch (action.type) {
case 'increment':
return {count: state.count + 1};
case 'decrement':
return {count: state.count - 1};
default:
throw new Error();
}
} function DemoComponent({ useReducer }: {
useReducer: UseReducer
}) {
const [ state, dispatch ] = useReducer(reducer, initialState);
const [ state2, dispatch2 ] = useReducer(reducer, {...initialState}); return (
<div>
Count: {state.count}
<Button onClick={() => dispatch({type: 'decrement'})}>-</Button>
<Button onClick={() => dispatch({type: 'increment'})}>+</Button>
<div></div>
Count2: {state2.count}
<Button onClick={() => dispatch2({type: 'decrement'})}>-</Button>
<Button onClick={() => dispatch2({type: 'increment'})}>+</Button>
</div>
)
}
  • useReducer时经过memo的存储,我们在队列结构里存下了值,当dispatch的时候,memo中的变量将会改变。

所以各位看出来了吗?

这就是hooks隐藏的逻辑所在!

重点来了~
hooks的本质是什么?

注:我的实现是为了理解它,与官方源码存在出入哈,理解原理就好~

Hooks本质看坑与规范

hooks的本质是-闭包。
它在外部作用域存储了值。
当setState发生时,整个函数(相当于render)都被触发调用,那么所有的变量就会重新赋值就像是下面的关键字:

  • const
  • let
  • function
    好多同学会觉得,奇怪了,为啥这个函数被重新调用,不会重新赋值呢?
    关键就是闭包!
    事实上hooks的每个setState都会导致function变成一个片段。在这之前的变量并不是之后的变量。
    而我们在onclick等事件中引用的函数,有可能会存储旧的变量引用!这就会导致一些大问题!!!
  • 例如内存泄漏
  • 例如值的引用无效
    那怎么解决?
    react其实提供了解决办法,就是useMemo useCallback等等。你可以再深入的去理解它。

Hooks使用大忌

1、 外部顶层变量问题,不要把全局变量放在函数外部,特别是你在开发公共组件的时候,你可以用useRef
2、依赖项不正确问题,依赖项不正确会导致闭包问题。
3、组件拆分太粗问题,函数不建议太长,500行以内最佳
4、盲目使用usexxx,要深刻理解才能更好的使用hooks

最后

高效编码;健康生活;

招人啦招人啦,SHEIN前端岗后端岗等你来投,可以私信博主,也可以扫码查看。期待与你做同事!

干货满满-原来这才是hooks-React Hooks使用心得的更多相关文章

  1. 初探React Hooks & SSR改造

    Hooks React v16.8 发布了 Hooks,其主要是解决跨组件.组件复用的状态管理问题. 在 class 中组件的状态封装在对象中,然后通过单向数据流来组织组件间的状态交互.这种模式下,跨 ...

  2. React hooks实践

    前言 最近要对旧的项目进行重构,统一使用全新的react技术栈.同时,我们也决定尝试使用React hooks来进行开发,但是,由于React hooks崇尚的是使用(也只能使用)function c ...

  3. 理解 React Hooks

    欢迎大家前往腾讯云+社区,获取更多腾讯海量技术实践干货哦~ 本文由志航发表于云+社区专栏 TL;DR 一句话总结 React Hooks 就是在 react 函数组件中,也可以使用类组件(classe ...

  4. React Hooks新特性学习随笔

    React Hooks 是 React 16.8 的新增特性.它可以让你在不编写 class 的情况下使用 state 以及其他的 React 特性. 前言 本篇主要以讲几个常用的api为主. 1.u ...

  5. react新特性 react hooks

    本文介绍的是react新特性react hooks,本文面向的是有一定react开发经验的小伙伴,如果你对react还不是很熟悉的话我建议你先学习react并多多联系. 首先我们都知道react有3种 ...

  6. 30分钟精通React今年最劲爆的新特性——React Hooks

    你还在为该使用无状态组件(Function)还是有状态组件(Class)而烦恼吗? --拥有了hooks,你再也不需要写Class了,你的所有组件都将是Function. 你还在为搞不清使用哪个生命周 ...

  7. React Hooks 深入系列 —— 设计模式

    本文是 React Hooks 深入系列的后续.此篇详细介绍了 Hooks 相对 class 的优势所在, 并介绍了相关 api 的设计思想, 同时对 Hooks 如何对齐 class 的生命周期钩子 ...

  8. React Hooks用法大全

    前言 在 React 的世界中,有容器组件和 UI 组件之分,在 React Hooks 出现之前,UI 组件我们可以使用函数,无状态组件来展示 UI,而对于容器组件,函数组件就显得无能为力,我们依赖 ...

  9. React Hooks究竟是什么呢?

    摘要: React Hooks原理解析. 原文:快速了解 React Hooks 原理 译者:前端小智 我们大部分 React 类组件可以保存状态,而函数组件不能? 并且类组件具有生命周期,而函数组件 ...

随机推荐

  1. zoj3777 Problem Arrangement(状压dp,思路赞)

    The 11th Zhejiang Provincial Collegiate Programming Contest is coming! As a problem setter, Edward i ...

  2. AtCoder Beginner Contest 182 D - Wandering (前缀和)

    题意:在\(x\)轴上,你刚开始在\(0\)的位置,第\(i\)次操作需要走\(A_1,...,A_i\)个单位,如果\(A_i\)为正向右走,否则向左走,求你所能走到的最大坐标. 题解:我们一步一步 ...

  3. hdu3033 I love sneakers!

    Problem Description After months of hard working, Iserlohn finally wins awesome amount of scholarshi ...

  4. hdu2126 Buy the souvenirs

    Problem Description When the winter holiday comes, a lot of people will have a trip. Generally, ther ...

  5. Codeforces Round #501 (Div. 3) B. Obtaining the String (思维,字符串)

    题意:有两个字符串\(S\)和\(T\),判断\(T\)是否能由\(S\)通过交换某位置的相邻字符得到,如果满足,输出交换次数及每次交换的位置,否则输出\(-1\). 题解:首先判断不满足的情况,只有 ...

  6. python代理池的构建2——代理ip是否可用的处理和检查

    上一篇博客地址:python代理池的构建1--代理IP类的构建,以及配置文件.日志文件.requests请求头 一.代理ip是否可用的处理(httpbin_validator.py) #-*-codi ...

  7. 字节笔试题 leetcode 69. x 的平方根

    更多精彩文章请关注公众号:TanLiuYi00 题目 解题思路 题目要求非负整数 x 的平方根,相当于求函数 y = √x 中 y 的值. 函数 y = √x  图像如下: 从上图中,可以看出函数是单 ...

  8. hihocoder 1631

    时间限制:1000ms 单点时限:1000ms 内存限制:256MB 描述 There are many homeless cats in PKU campus. They are all happy ...

  9. SMB relay

    SMB relay 0x00 SMB服务 先来了解一下什么是 SMB 服务,SMB(Server Message Block)是一个协议名称,用它可以共享计算机之间的文件.打印机.串口等,通过 SMB ...

  10. certbot 获取数字证书失效问题

    title: certbot 获取数字证书失效问题 author: Narule date: 2021-02-18 10:45:00 +0800 categories: [Technology^技术, ...