干货满满-原来这才是hooks-React Hooks使用心得
序言
---最后有招聘信息哦~
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);
函数式的特点
- 函数是"第一等公民" 
- 只用"表达式",不用"语句" 
"表达式"(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使用心得的更多相关文章
- 初探React Hooks & SSR改造
		Hooks React v16.8 发布了 Hooks,其主要是解决跨组件.组件复用的状态管理问题. 在 class 中组件的状态封装在对象中,然后通过单向数据流来组织组件间的状态交互.这种模式下,跨 ... 
- React hooks实践
		前言 最近要对旧的项目进行重构,统一使用全新的react技术栈.同时,我们也决定尝试使用React hooks来进行开发,但是,由于React hooks崇尚的是使用(也只能使用)function c ... 
- 理解 React Hooks
		欢迎大家前往腾讯云+社区,获取更多腾讯海量技术实践干货哦~ 本文由志航发表于云+社区专栏 TL;DR 一句话总结 React Hooks 就是在 react 函数组件中,也可以使用类组件(classe ... 
- React Hooks新特性学习随笔
		React Hooks 是 React 16.8 的新增特性.它可以让你在不编写 class 的情况下使用 state 以及其他的 React 特性. 前言 本篇主要以讲几个常用的api为主. 1.u ... 
- react新特性 react hooks
		本文介绍的是react新特性react hooks,本文面向的是有一定react开发经验的小伙伴,如果你对react还不是很熟悉的话我建议你先学习react并多多联系. 首先我们都知道react有3种 ... 
- 30分钟精通React今年最劲爆的新特性——React Hooks
		你还在为该使用无状态组件(Function)还是有状态组件(Class)而烦恼吗? --拥有了hooks,你再也不需要写Class了,你的所有组件都将是Function. 你还在为搞不清使用哪个生命周 ... 
- React Hooks 深入系列 —— 设计模式
		本文是 React Hooks 深入系列的后续.此篇详细介绍了 Hooks 相对 class 的优势所在, 并介绍了相关 api 的设计思想, 同时对 Hooks 如何对齐 class 的生命周期钩子 ... 
- React Hooks用法大全
		前言 在 React 的世界中,有容器组件和 UI 组件之分,在 React Hooks 出现之前,UI 组件我们可以使用函数,无状态组件来展示 UI,而对于容器组件,函数组件就显得无能为力,我们依赖 ... 
- React Hooks究竟是什么呢?
		摘要: React Hooks原理解析. 原文:快速了解 React Hooks 原理 译者:前端小智 我们大部分 React 类组件可以保存状态,而函数组件不能? 并且类组件具有生命周期,而函数组件 ... 
随机推荐
- 【POJ 2411】【Mondriaans Dream】  状压dp+dfs枚举状态
			题意: 给你一个高为h,宽为w的矩阵,你需要用1*2或者2*1的矩阵填充它 问你能有多少种填充方式 题解: 如果一个1*2的矩形横着放,那么两个位置都用二进制1来表示,如果是竖着放,那么会对下一层造成 ... 
- Codeforces Round #575 (Div. 3) D2. RGB Substring (hard version)
			传送门 题意: 给你一个长为n的仅由'R','G','B'构成的字符串s,你需要在其中找出来一个子串.使得这个子串在"RGBRGBRGBRGB........(以RGB为循环节,我们称这个串 ... 
- CodeForces - 220B  离散化+莫队算法
			莫队算法链接:传送门 题意: 有n个数,m个区间.问区间内有多少个x,x满足x的个数等于x的值的个数(如果x是3,区间内要存在3个3). 题解: 因为a[i]太大,所以要离散化一下,但是不能用map容 ... 
- ThreadLocal使用全解
			一.何为ThreadLocal 1.ThreadLocal的含义 ThreadLocal,即线程变量,是一个以ThreadLocal对象为键,任意对象为值的存储结构.这个结构被附带在线程上,也就是说一 ... 
- 国产网络损伤仪 SandStorm -- 什么是链路规则?
			"链路规则"是网络损伤仪SandStorm(又名弱网测试仪)里面非常重要的功能,主要用于不同仿真链路之间的选择. 如下图的所示: ... 
- Linux命令之find命令中的-mtime参数
			有关find -mtime的参数解释 mtime参数的理解应该如下: -mtime n 按照文件的更改时间来找文件,n为整数. n表示文件更改时间距离为n天, -n表示文件更改时间距离在n天以内,+n ... 
- 二进制安装kubernetes(五) kubelet组件安装
			概述资料地址:https://blog.csdn.net/bbwangj/article/details/81904350 Kubelet组件运行在Node节点上,维持运行中的Pods以及提供kube ... 
- cccc超级酱油心得
			第一次线下比赛献给了cccc. 大致写写自己心得,比赛前一天晚上日常和室友在宿舍玩到11点多,洗漱上床.睡前突然想起第二天还有比赛,顿时激动加紧张.在床上刷了刷知乎和百度,看几道去年的真题,熬到了12 ... 
- Leetcode(38)-报数
			报数序列是指一个整数序列,按照其中的整数的顺序进行报数,得到下一个数.其前五项如下: 1. 1 2. 11 3. 21 4. 1211 5. 111221 1 被读作 "one 1&quo ... 
- linux下新建用户
			新建用户的两种方式: 一步步创建 useradd -m user1 #-m 是建立家目录 passwd user1 #设置密码 usermod -a -G root user1 #加入管理员 chsh ... 
