1. 首页

react hook用法及实现原理

1、hook的简介

Hook 是 React 16.8 的新增特性。它可以让你在不编写 class 的情况下使用 state 以及其他的 React 特性。

直接贴code,hook实现简版计数器

react hook用法及实现原理

简单debugger下代码

react hook用法及实现原理

  • 我们可以看到hook是用memorizedState来保存状态

react hook用法及实现原理

2、useState的简版实现

  • 核心作用是给函数组件增加了一个保持状态的功能

let memoizedState; //声明memoizedState function useState(initialState) { memoizedState = memoizedState || initialState; function setState(newState) { memoizedState = newState; //设置状态时候把新状态赋值给memoizedState render(); //重新render } return [memoizedState,setState] } // 前端进阶资源教程 https://www.javascriptc.com/

从简版的实现来看,还是很容易理解,测试下效果

react hook用法及实现原理

3、userReducer简介和实现

3.1、userReducer简介

  • useState 的替代方案。它接收一个形如 (state, action) => newState 的 reducer,并返回当前的 state 以及与其配套的 dispatch 方法。(如果你熟悉 Redux 的话,就已经知道它如何工作了。)
  • 在某些场景下,useReducer 会比 useState 更适用,例如 state 逻辑较复杂且包含多个子值,或者下一个 state 依赖于之前的 state 等。

用userReducer实现计数器,可接收3个参数,reducer | initalArg(初始值) | init (定义初始值的函数)


import React,{Fragment,useReducer} from 'react'; import ReactDOM from 'react-dom'; // reducer,跟redux中reducer一样 const INCREMENT = "INCREMENT"; const DECREMENT = "DECREMENT"; function reducer(state,action){ switch (action.type) { case INCREMENT: return {number:state.number+1}; case DECREMENT: return {number:state.number-1}; default: return state; } } //初始值 let initalArg = 0; // 返回初始值的函数 function init(initalArg) { return {number:initalArg}; } function Counter() { // state = {number:0} const [state,dispatch] = useReducer(reducer,initalArg,init); return ( <Fragment> <p>{state.number}</p> <button onClick={()=>dispatch({type:INCREMENT})}>+</button> <button onClick={()=>dispatch({type:DECREMENT})}>-</button> </Fragment> ) } function render() { ReactDOM.render(<Counter />,document.getElementById('root')); } render(); // 前端进阶资源教程 https://www.javascriptc.com/

3.2、简版实现原理


let memoizedState; //声明记忆的状态 function useReducer(reducer,initalArg,init) { let initialState; // init如果没有传值,initalArg为默认的初始状态。如果传值,初始值函数处理后作为初始状态 if(typeof init !='undefined'){ initialState = init(initalArg); }else { initialState = initalArg; } memoizedState = memoizedState || initialState; function dispatch(action) { memoizedState = reducer(memoizedState,action); render(); } return [memoizedState,dispatch] } // 前端进阶资源教程 https://www.javascriptc.com/

运行结果

react hook用法及实现原理

3.3、useReducer是useState的内部实现,重写useState实现


let memoizedState; function useReducer(reducer,initalArg,init) { let initialState; if(typeof init !='undefined'){ initialState = init(initalArg); }else { initialState = initalArg; } memoizedState = memoizedState || initialState; function dispatch(action) { memoizedState = reducer(memoizedState,action); render(); } return [memoizedState,dispatch] } function useState(initialState) { // 主要是reducer实现,把新状态赋值过去 return useReducer((oldState,newState)=>newState,initialState); } // 前端进阶资源教程 https://www.javascriptc.com/

验证以下

react hook用法及实现原理

4、多个useState同时调用

当一个组件调用多个useState时,此时我们需要用数组来保存多个初始值

4.1、多个useState使用的示例demo

  • 两个按钮,一个改变name,一个改变number

react hook用法及实现原理

4.2、实现原理

  • 之前都是使用一个useState,当多个useState时候,需要用数组保存所有的初始状态
  • 需要用index记录当前的索引
  • 每次render时候,index索引需要回复初始值

import React,{Fragment} from 'react'; import ReactDOM from 'react-dom'; // 数组保存memoizedState let memoizedState=[]; // 记录索引 let index = 0; function useState(initialState) { memoizedState[index] = memoizedState[index] || initialState; // 缓存当前索引,因为每次render,index索引会重置为0 let currentIndex = index; function setState(newState) { memoizedState[currentIndex] = newState; render(); } return [memoizedState[index++],setState] } function Counter() { const [name,setName] = useState('计数器'); const [number,setNumber] = useState(0); return ( <Fragment> <p>{name }:{number}</p> <button onClick={()=>setName("计数器"+Date.now())}>改名称</button> <button onClick={()=>setNumber(number+1)}>+</button> </Fragment> ) } function render() { // 每次render,把index回复初始值 index = 0; ReactDOM.render(<Counter />,document.getElementById('root')); } render(); // 前端进阶资源教程 https://www.javascriptc.com/

看下效果,源码是用链表实现的,此处我们用数组,逻辑是差不多,容易理解

react hook用法及实现原理

5、useEffect简介与实现

5.1、简介

  • useEffect 给函数组件添加了操作副作用的能力, 比如事件的订阅与取消、定时器的设置与清空
  • 类似于在类组件生命周期 componentDidMount、componentWillUnmount做的事情

计数器变化后,实现打印一句log的示例 参考链接


import React,{Fragment,useState} from 'react'; import ReactDOM from 'react-dom'; function Counter() { const [name,setName] = useState('计数器'); const [number,setNumber] = useState(0); useEffect(()=>{ // 订阅 console.log("订阅状态") },[number,name]); return ( <Fragment> <p>{name }:{number}</p> <button onClick={()=>setName("计数器"+Date.now())}>改名称</button> <button onClick={()=>setNumber(number+1)}>+</button> </Fragment> ) } function render() { ReactDOM.render(<Counter />,document.getElementById('root')); } render(); // 前端进阶资源教程 https://www.javascriptc.com/

5.2、简版实现

  • useEffect第二个参数为依赖项,即当依赖项改变时,才会触发回调

简版实现


// 记录最后依赖项 let lastDependencies; function useEffect(callback,dependencies) { // 如果依赖项没有传值,则直接调用callback if(!dependencies) return callback(); /* 1、首次渲染isChange为true,把初始的依赖项赋给lastDependencies * 2、再次渲染时候,把lastDependencies和dependencies做对比,当不完全相等时,才触发回调 */ let isChange = lastDependencies? !dependencies.every((item,index)=>item===lastDependencies[index]):true; if(isChange){ callback(); lastDependencies = dependencies; } } // 前端进阶资源教程 https://www.javascriptc.com/

5.3、当有多个useEffect时,如何实现

  • 统一把lastDependencies放到useState中的memoizedState中

let memoizedState=[]; let index = 0; function useState(initialState) { memoizedState[index] = memoizedState[index] || initialState; let currentIndex = index; function setState(newState) { memoizedState[currentIndex] = newState; render(); } return [memoizedState[index++],setState] } function useEffect(callback,dependencies) { console.log('dependencies',dependencies); // 如果依赖项没有传值,则直接调用callback if(!dependencies){ // 保证索引对应 index++; return callback(); } // 从memoizedState取最后一个依赖项 let lastDependencies = memoizedState[index]; let isChange = lastDependencies? !dependencies.every((item,index)=>item===lastDependencies[index]):true; if(isChange){ callback(); // 往memoizedState存依赖项 memoizedState[index] = dependencies; } // 索引递增 index++ } // 前端进阶资源教程 https://www.javascriptc.com/

验证下效果

react hook用法及实现原理

后续继续补充

作者:言sir
链接:https://juejin.im/post/5d78b196f265da03d55e8313

看完两件小事

如果你觉得这篇文章对你挺有启发,我想请你帮我两个小忙:

  1. 关注我们的 GitHub 博客,让我们成为长期关系
  2. 把这篇文章分享给你的朋友 / 交流群,让更多的人看到,一起进步,一起成长!
  3. 关注公众号 「画漫画的程序员」,公众号后台回复「资源」 免费领取我精心整理的前端进阶资源教程

JS中文网是中国领先的新一代开发者社区和专业的技术媒体,一个帮助开发者成长的社区,目前已经覆盖和服务了超过 300 万开发者,你每天都可以在这里找到技术世界的头条内容。欢迎热爱技术的你一起加入交流与学习,JS中文网的使命是帮助开发者用代码改变世界

本文著作权归作者所有,如若转载,请注明出处

转载请注明:文章转载自「 Js中文网 · 前端进阶资源教程 」https://www.javascriptc.com

标题:react hook用法及实现原理

链接:https://www.javascriptc.com/3817.html

« React(hook)+Typescript+Antd 实现一个可配置的后台管理系统
how to use react hook【编辑中】»
Flutter 中文教程资源

相关推荐

QR code