navigation

[react] Hooks

[Package 01- Design Pattern] Design Principles and Factory Pattern (Simple Abstract method) Adapter pattern Decorator pattern [Package 02- Design Pattern] Command pattern Share pattern Composition pattern Proxy pattern

[React from Zero practice 01- background] code split [React from zero practice 02- background] permission control [React from zero practice 03- background] custom hooks [React from zero practice 04- background] docker-compose React +egg+nginx+mysql [react from zero practice 05- background

AST Abstract syntax tree [source-webPack02-pre knowledge] Tapable [source-webpack03] Hand-written Webpack-compiler simple compilation process [source] Redux React-redux01 [source] axios [source] vuex [source-vue01] data Responsive and initialized render [source-vue02] computed responsive – initialized, accessed, Update process [source-vue03] Watch listener – Initialize and update [source-vue04] vue. set and vm.$set [source-vue05] vue.extend

Vue.nextTick and vm.$nextTick

[source code -react01] ReactDOM. Render01 [source code -react02] handwritten hook scheduling -useState implementation

[Deployment 01] Nginx [Deployment 02] Docker Deployment Vue Project [Deployment 03] Gitlab-CI

Data Structures and Algorithms 01 binary lookup and sorting

[Delve 01] Execution context [Delve 02] Prototype chain [Delve 03] Inheritance [Delve 04] event loops [Delve 05] Currization partial functions Function memory [Delve 06] Implicit conversions and operators [Delve 07] Browser caching mechanism (HTTP caching mechanism) [Delve 08] front-end security [In Depth 09] Deep copy [in depth 10] Debounce Throttle [in depth 11] Front-end routing [in depth 12] Front-end modularization [in depth 13] Observer mode Publish subscribe mode Bidirectional data binding [in depth 14] Canvas [in depth 15] webSocket [in-Depth 16] Webpack [in-Depth 17] HTTP and HTTPS [in-Depth 18] CSS-Interview [in-Depth 19] Handwritten Promise [In-depth 20] Handwritten Functions [In-depth 21] Data Structures and Algorithms – Binary Lookup and Sorting [In-depth 22] Js and V8 garbage Collection mechanisms [in-Depth 23] JS design patterns – Agents, Policies, singleton [in-Depth 24] Fiber

[Front-End learning java02-SpringBoot actual] environment configuration and HelloWorld service [front-end learning java02-SpringBoot actual] mybatis + mysql implementation song add, delete, change to check [front-end learning java03-SpringBoot actual] Lombok, log, Deployment [front-end learning Java04 -SpringBoot actual practice] static resources + interceptor + front and back end file upload [front-end learning JAVa05 -SpringBoot actual practice] common notes + Redis to achieve statistical function [front-end learning Java06 -SpringBoot actual practice] injection + Swagger2 3.0 + Unit test JUnit5 [Front-End learning Java07 -SpringBoot practice] IOC scanner + transaction + Jackson [front-end learning Java08 -SpringBoot practice summary 1-7] stage summary [front-end learning Java09 -SpringBoot actual combat] multi-module configuration + Mybatis-plus + single multi-module package deployment [front-end learning Java10 -SpringBoot actual combat] Bean assignment conversion + parameter verification + global exception processing [front-end learning Java11-SpringSecurity] configuration + Memory + database = three ways to implement RBAC [front-end learning Java12-SpringSecurity] JWT [front-end learning Java13-SpringCloud] Eureka + RestTemplate + Zuul + Ribbon

(1) Pre-knowledge

(1) Some words

// Passive destructure // passive destructure // passive destructure // Dar structure objects and arrays persist // usePersistFn persist some functions imperative urgent // useImperativeHandle + forwardRef delegation // Fetch -option-credentials: Omit omit metrics metrics intervalCopy the code

(2) the fetch and XMLHttpRequest

  • Fetch and XMLHttpRequest set cORS across domains - to carry cookies
    • ajax => xhr.withCredentials = true
    • fetch => option.credentials = ‘include’ | ‘same-origin’ | ‘omit’
      • Cross domain = > ‘include’
      • Only want to send credentials if the request URL is at the same origin as the calling script => ‘same-origin’
      • No credentials => ‘omit’
      • 8. Omit the previous information
  • fetch
    • Semantically concise, promise based implementation, supports async await, and returns a Promise object
  • The response object
    • Property Header Status statusText
    • type
      • Response.json () ——– JSON data
      • Response.text () ——– text data, such as requesting a static resource file of type.md
      • The response. The blob () — — — — — — — —
      • The response. FormDate () – form

(3) the DOM – events

  • 2021/08/15 review

  • EventTarget interface
    • (DOM event operations) are defined on (EventTarget interface)
    • The EventTarget interface provides three main instance methods
      • addEventListener(type, listener[, useCapture])
        • Bind the listener function for the event
        • parameter
          • Type Event name – Case sensitive
          • Listener Listener function – called when an event is triggered
          • UseCapture whether capture fires (optional) – The default is false, indicating that it fires during the bubble phase
        • Parameter Note
          • How to trigger an event only once? once )
          • Second argument: in addition to being a listener function, it can also be (an object with a handleEvent method)
          • Third argument: In addition to (useCapture), can also be a (property configuration object)
            • Capture: Indicates whether Boolean is triggered during the capture phase
            • Once: indicates whether the listener raises a Boolean only once
            • Passive: Indicates that the listener will not call the preventDefault method
        • this
          • This in the listener refers to the object to which the event is bound.
      • removeEventListener
        • Function: Removes an event listener added by the addEventListener method
        • Pay attention to the point
          • RemoveEventListener Validity conditions
          • Must bind to addEventListener (same DOM node) and (same parameter)
      • dispatchEvent
        • Fires (the specified event) at (the current node), thus firing (the listener function)
        • Target.dispatchevent (event) – The parameter is an instance of the event object
  • Event binding listens to three methods of functions
    • The HTMLon-attribute
      • On – The value of the property is (the code to be executed, not a function, to execute the function, add ()).
      • It only triggers during the bubbling phase
      • Setting on- directly has the same effect as setting on- through the element node (setAttribute) method
    • The event attribute of the element node
      • It only triggers during the bubbling phase
      • Property corresponds to a (function name)
    • addEventListener
  • Advantages and disadvantages of the three listener functions for bound events
    • On – : violates the separation of HTML from javascript and can only be triggered during the bubble phase, specifying the code to be executed
    • The event attribute of the element node: only one listener can be bound and can only be emitted during the bubble phase
    • AddEventListener :(the same event can be bound to multiple listener functions) (can specify the trigger stage, set to trigger once)
  • Transmission of events
    • CapturePhase capturePhase: the window – > document – > HTML – > bodu – >… The target node
    • TargetPhase targetPhase: Triggers on the destination node
    • BubblingPhase bubblingPhase: Target node -> Window
  • Event Proxy – Delegation
  • The Event object
    • targetcurrentTarget
      • Target: represents (the node that triggers the event first)
      • CurrentTarget: represents (the node bound to the event listener function)
    • preventDefault
      • (event.preventDefault) for browser (default behavior for current Event)
      • Cancelable = true
      • Simply undoing the default behavior does not prevent propagation
      • Preventing propagation is stopPropagation + stopPropagation
      • Can be used to do text box verification, verification failed to cancel the default text selection
    • stopPropagation
      • (event.stoppropagaton) prevents events from propagating in the DOM
        • Stoppropagation () prevents listening functions defined for the same event on other nodes from being fired, but does not include other listening events on the current node, and does not include other listening functions for the same event
        • To stop event propagation completely, use stopImmediatePropagation()
    • stopImmediatePropagation
      • If other listeners for the same event are blocked from being called, other listeners for the same event are blocked
  • CustomEvent CustomEvent interface
    • The CustomEvent interface is used to generate (CustomEvent instances)
    • What does a custom event do?
      • Event predefined by the browser. Data cannot be bound to the event
      • (Custom event) can be passed in (specified data)
      • const eventInstance = new CustomEvent(type, options)

(4) the list

  • concept
    • (linked list) to link (scattered memory) together by (Pointers)
  • The characteristics of
    • Linked lists are discontinuous, so they do not require a contiguous block of storage
  • node
    • Each memory block is a node
    • To wire each node together, each node not only (Store the data) and (Record the address of a node )
  • Successor pointer next
    • The pointer that records the next node is called the successor pointer next
  • Singly linked lists
    • A singly linked list is a linear list that is ordered in one direction
    • A special node in a singly linked list
      • First node:Head node– Records linked listsBase address
      • Last node:End node– Pointer pointingEmpty address Null
    • If you determine whether a node is a tail node
      • We can determine whether a node is a tail node by checking whether the successor pointer next is Null
    • What is the time complexity of single linked list insertion and deletion?
      • O(1)
      • This is the theoretical value. There are some preconditions
      • The time complexity of the search is first iteratedO(n), and then the time complexity of adding and deleting isO(1)= >O(n)
    • Node composition
      • (Data field) and (pointer field)
  • Two-way linked list
    • A bidirectional list can be traversed bidirectionally, more flexible
    • Bidirectional lists are more efficient at (deleting, inserting) nodes
  • Circular linked list
    • A circular linked list is a kind of (Special single linked list )
    • The difference between circular and single linked lists
      • Circular list (last node) pointer to (head node) instead of (Null address)

(two) JS – to achieve a single linked list

  • Js implementation of one-way linked list – source
// js implementation - [unidirectional list] // unidirectional list, For each element there is a node that stores the element itself and a node that points to the reference of the next element (el) { this.el = el; This. next = null; / / pointer}; this.head = null; This.length = 0; this.length = 0; this.length = 0; // add node this.append = (el) => {let node = new NodeConstructor(el); let current = null; // if (! Enclosing the head) {/ / -- -- -- -- -- -- -- -- -- -- -- -- -- -- the head node does not exist, this generates a node. The head = node; } else {/ / -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- the head nodes exist, find the last node and pointed to the last node of next to insert the node of the current = this. The head; While (current. Next) {current = current. Next; Lastnode. next = null // find the lastNode and point next to node. } current. Next = node; } this.length++;} length.length+;} length.length+; // After adding nodes, add length +1}; First, we need to consider the boundary conditions. If the insertion position is in the head or larger than the tail position, we do not need to go through the processing again. This. insert = (position, el) => {const node = new NodeConstructor(el); let prevNode = null; let current = this.head; let currentIndex = 0; If (position >= 0 && position < this.length) {if (position === 0) {// Insert header node.next = current; // Next => head this.head = node; Head} else {// This is not the last one, because there is no flag pointer to the last one, While (currentIndex++ < this.position) {prevNode = current; // current = current. Next; } node.next = current; Next => current prevNode.next = node; Next => node this.length++; Return true; }} else {return false; }}; This.indexof = (el) => {let currentIndex = 0; this.index = (el) => {let currentIndex = 0; let current = this.head; while (currentIndex < this.length) { if (current.el === el) { return currentIndex; } current = current. Next; // otherwise find the next currentIndex++; // index+1 } }; This.removeat = (position) => {this.removeat = (position) => {this.removeat = (position) => {// position indicates the position, If (position >= 0 && position < this.length) {let prevNode = null; // Cache the current node. Let current = this.head; // let currentIndex = 0; If (position === 0) {this.head = current. Next; } else {// If the first node of the list is deleted, the second node is assigned to the first node (head = head.next). Position === length-1 while (currentIndex++ < position) {prevNode = current; current = current.next; } prevNode.next = current. Next; Next === null; next == null; Prevnode.next = current. Next = null} this.length--; // Complete, length -1 return current. El; // return data on the deleted node el} else {return null; // Return a random value, indicating that no operation has been done, because the position is not on the list}}; // Remove the specified node // remove the specified node implementation is very simple, we just need to use the previous good search node to find the location of the node, This.remove = (el) => {let index = this.indexof (el); this.removeAt(index); }; Boolean this.isEmpty = () => {return this.length === 0; }; This.size = () => {return this.length; }; This.toarray = () => {let current = this.head; let resultList = []; While (current.next) {// If next exists, add el to the array until the last node, resultList.push(current.el); current = current.next; // next} return resultList; }; } const linkedList = new LinkedList(); // head(el:100) -> next(el: 200) -> next(null) linkedList.appEnd (100); linkedList.append(200); linkedList.append(300); linkedList.append(400); linkedList.append(500); linkedList.append(600); linkedList.append(700); // Test indexOf const index = linkedList.indexof (300); console.log(`index`, index); // Test removeAt linkedList.removeat (1); // test remove linkedlist.remove (600); // test isEmpty const isEmpty = linkedlist.isempty () console.log(' isEmpty ', IsEmpty) // test size const size = linkedlist.size () console.log(' size ', // Test toArray const arr = linkedlist.toarray () console.log(' arr ', arr)Copy the code

(3) hand-written hook-useState implementation

  • Handwritten hook scheduling -useState implementation – source repository
  • Handwritten hook scheduling -useState implementation – Mind mapping

// 手写hooks源码 - useState

// (一)
// useState工作原理
// 1. 产生了更新,更新的内容保存在update对象中,更新会让组件重新render
// 2. 组件render时,useState调用后返回值数组,数组的第0项count为更新后最新的值
// 3. 更新分为两种
//    - mount: 初始化mount阶段时,count是useSate的参数即 initialState
//    - update: 点击事件等触发dispatcher函数,即useState的返回值数组的第二个成员函数,导致count更新为 count => count + 1

// (二)
// hooks中的一些概念
// 1. 链表
//    hook.memoizedState => 单向链表 - (hook对象,fiber对象) => hooks函数本的值身会保存在链表结构中,比如 [count, setCount] = useState(0) 中的 count
//    hook.queue =========> 环状链表 - (update对象) =========> 多个useState返回数组中的第二个成员setter函数( 更新执行的函数 ),也会保存在链表中,是一个环状链表,环状链表是特殊的单链表

let workInProgressHook = null;
// workInProgressHook
// 1. 第一种情况: workInProgressHook 指向当前正在计算的hook,相当于一个指针,时刻修改指向
// 2. 第二种情况: 执行到 schedule() 时,workInProgressHook 指向保存在 fiber.memoizedState 中的整个单向链表
// 3. 总结:
//    - workInProgressHook: 可能指向最新的hook对象,也有可能指向整个hook对象组成的整个单向链表
//    - fiber.memoizedState: 始终指向整个hook对象组成的单向链表

let isMount = true; // 标志位,boolean,表示是否是 ( mount ) 阶段,对应的有 ( mount update ) 两个阶段

// fiber节点
const fiber = {
  stateNode: App, // fiber.stateNode指向真实的DOM,即指向fiberRoot
  memoizedState: null, // hooks链表,memoizedState保存的是kook对象,用来保存function函数组件中的state数据,比如setState返回数组的第一个成员的值,本例中是count的值
};

// schedule
// 调度
function schedule() {
  workInProgressHook = fiber.memoizedState;
  // workInProgressHook
  // 1. 调度方法中的更新方法执行前,将workInProgressHook重置为fiber中的第一个hook,这样才能保证mount周期后,update周期遍历链表是从头往后遍历的
  // 2. 为什么是第一个hook,因为 fiber.memoizedState 是一整条链表,本身就是链表的第一个节点,叫做头节点

  const app = fiber.stateNode(); // 触发组件更新,即执行functionComponent
  isMount = false; // 第一次调度后,不再是mount节点,以后将是render阶段
  return app;
}

// dispatchAction
// 1. dispatchAction 就是 useState 的 setter 函数
const dispatchAction = (queue, action) => {
  const update = {
    action,
    next: null,
  };

  if (queue.pending === null) {
    update.next = update; // 初始化时,环状链表只有一个节点,所以 u0 -> u0
    // 1. 第一次
    // 1. 环状链表是 u0 -> u0
    // 1. 对应代码是 update.next = update
    // 2. 接下来代码 queue.pending = update;
  } else {
    update.next = queue.pending.next;
    queue.pending.next = update;
    // 1. 非第一次
    // 1. 非第一次链表已经存在,因为第一次初始化得到的链表是u0,现在插入节点u1
    // 2. 环状链表是 u1 -> u0 -> u1
    // 2. u1->u0 对应的代码是 ( u1的update.next = u0的update.next还是u0,因为第一次u0->u0 ) 而 ( queue.pending = 上一次的update ) 所以 ( update.next = queue.pending.next )
    // 2. u0->u1 对应的代码是 ( u0的update.next = u1的update ) 即 ( queue.pending.next = update)
    // 3. 接下来代码的代码是 queue.pending = update; 即将 queue.pending指针指向最新的update,这里是u1,在下一次开始时成为旧的update,也就是环状链表的最后一个节点
  }

  queue.pending = update;
  // queue.pending
  // 1. 初始化时,queue.pending = update,即 queue.pending = update.next = update -> u0
  // 2. 本次dispatchAction调用update是最新的update,在下一次新的dispatchAction调用时,update将成为旧的update

  schedule(); // dispatch执行后,执行调度函数schedule,更新组件
};



function useState(initialState) {
  let hook;

  if (isMount) {
    // ----------------------------------------- mount
    hook = {
      memoizedState: initialState, // 保存初始值,即 useState 的参数
      queue: {
        pending: null, // 环形链表,用来保存update组成的链表数据,update对象上有action属性,该属性就是 const [count, setCount] = useState(0) 的 setCount 函数
      },
      next: null,
    };
    if (!fiber.memoizedState) {
      // mount阶段时,即初始化时 fiber.memoizedState = null
      fiber.memoizedState = hook; // 将 fiber.memoizedState 指向最新的 hook
    } else {
      //  fiber.memoizedState存在,说明在mount阶段中有多个 useState
      workInProgressHook.next = hook; // 将上一个hook的next指向当前的hook,即 prevHook.next -> hook,所以单链表中新节点是在旧节点之后
      // 1. mount阶段,第一个hook,第一次未进入时: workInProgressHook = fiber.memoizedState = hook
      // 2. mount阶段,第二个hook,会进入else:  workInProgressHook.next === fiber.memoizedState.next
    }
    workInProgressHook = hook; // 移动workInProgressHook的指针,从新将 workInProgressHook 指向最新的 hook,即 workInProgressHook 始终指向最新的 hook
  } else {
    // ----------------------------------------- update
    // 1. 更新时,hook对象已经存在,而 workInProgressHook 上保存的整个hook对象组成的链表
    // 2. 问题:为什么是整个链表?
    //    回答:因为更新执行 setCount => 触发 schedule() => 在更新 schedule() 调度开始时,都要把指针重新指向hook节点组成的单向链表
    // 3. 注意点:workInProgressHook 是一个指针,是会变的,可能指向当前最新的正在执行hook函数的hook对象节点,也可能指向这个那个单向链表

    hook = workInProgressHook; // ---------------------1.完整的hook链表
    workInProgressHook = workInProgressHook.next; // --2.注意这里workInProgressHook又会马指向最新的hook对象,每调用一次setCount,就往后移动一个节点从而找到当前的hook对象,从而知道现在是在处理哪个hook,即不断往后遍历,用于下一次遍历时就行赋值给hook ( 即hook = workInProgressHook )
  }

  let baseState = hook.memoizedState; // 当前,正在计算的hook的state数据,注意,此时还没有进行 dispatchAction
  if (hook.queue.pending) {
    let firstUpdate = hook.queue.pending.next; // 找到环形链表中的第一个update对象节点

    do {
      const action = firstUpdate.action;
      baseState = action(baseState); // 执行update对象上的action方法
      // action
      // 1. setCount((count) => count + 1) 对应 dispatchAction.bind(null, hook.queue) === dispatchAction = (queue, action) { ...}
      //    - dispatchAction.bind(null, hook.queue) 返回的新的函数就是 setCount,并且dispatchAction的第一个参数被固定成了hook.queue,setCount只需要传入第二个参数即可
      //    - dispatchAction(queue, action)
      //    - dispatchAction(hook.queue, (count) => count + 1) 所以 update.action 就是 (count) => count + 1

      // 2. baseState = action(baseState)
      //    - 就的state传入函数,返回值在赋值给 baseState,成为最新的state的值

      firstUpdate = firstUpdate.next; // 得到最新计算的state后,指针后移一位,继续执行下一个 setState
    } while (firstUpdate !== hook.queue.pending); // 最后一个update执行完后跳出循环

    hook.queue.pending = null; // 遍历完所有的 setCount 之后,清空环形链表
  }

  hook.memoizedState = baseState; // 再保存最新的state
  // ( 最新的state ) 赋值给 ( hook.memoizedState )
  // baseState 可能是新计算的值,也有可能是旧值,取决于上面的是否进入上面的 hook.queue.pending 的if语句中

  return [baseState, dispatchAction.bind(null, hook.queue)];
}

function App() {
  const [count, setCount] = useState(0);
  const [count2, setCount2] = useState(2);

  console.log(`isMount => `, isMount);
  console.log(`count,count2 =>`, count, count2);

  return {
    click: () => {
      setCount((count) => count + 1)
      setCount((count) => count + 2)
    },
  };
}

window.app = schedule();
Copy the code

UseLocalStorageState custom hook-ahooks source code

  • UseLocalStorageState source code – warehouse address
  • useLocalStorageState
// 1 useLocalStorageState // A Hook that can persist state in localStorage. const useLocalStorageState = createUseStorageState( typeof window === 'object' ? LocalStorage: null, // window exists, pass window.localStorage // otherwise pass null); export default useLocalStorageState;Copy the code
  • createUseStorageState
//createUseStorageState export function createUseStorageState(nullishStorage: Storage | null) { function useStorageState<T = undefined>(key: string): StorageStateResult<T>; / / only one parameter function useStorageState < T > (/ / two parameters is the key: string, defaultValue: T | IFuncUpdater < T >,) : StorageStateResultHasDefaultValue<T>; function useStorageState<T>(key: string, defaultValue? : T | IFuncUpdater<T>) { const storage = nullishStorage as Storage; const [state, setState] = useState<T | undefined>(() => getStoredValue()); useUpdateEffect(() => { setState(getStoredValue()); }, [key]); // const useUpdateEffect: typeof useEffect = (effect, deps) => { // const isMounted = useRef(false); // useEffect(() => { // if (! isMounted.current) { // isMounted.current = true; // } else { // return effect(); // } // }, deps); / /}; Row does not exist // - 1. DefaultValue is a function called and returns the result of execution // - 2. Function getStoredValue() {const raw = storage.getitem (key); Parse (raw); // Raw: raw if (raw) {try {return JSON. Parse (raw); // Data corresponding to the key exists in the storage. Parse and return} catch (e) {}} if (isFunction<IFuncUpdater<T>>(defaultValue)) {// 1 // if // // 2 // defaultValue // -uselocalstoragestate (), the second argument to initialize the defaultValue return defaultValue(); } return defaultValue; } const updateState = useCallback( (value? : = > {T | IFuncUpdater < T >) if (typeof value = = = 'undefined') {/ / 1. Undefined / / - storage removal / / updateState () or updateState(unfined) // - state undefined storage.removeItem(key); setState(undefined); } else if (isFunction<IFuncUpdater<T>>(value)) { // value = (prevState: T) => T // 2. function // -storage store new value - new value is the return value of the function call value(previousState) // -state const previousState = getStoredValue(); const currentState = value(previousState); storage.setItem(key, JSON.stringify(currentState)); setState(currentState); } else {// 3. Undefined and function // -storage store new value // -state value storage.setitem (key, json.stringify (value)); setState(value); } }, [key], ); return [state, updateState]; } if (! Return function (_: string, defaultValue:) {// localStorage does not exist. any) { return [ isFunction<IFuncUpdater<any>>(defaultValue) ? defaultValue() : defaultValue, () => {}, ]; } as typeof useStorageState; } return useStorageState; }Copy the code
  • useUpdateEffect
// useUpdateEffect // - componentDidUpdate, const useUpdateEffect: typeof useEffect = (effect, deps) => { const isMounted = useRef(false); useEffect(() => { if (! IsMounted. Current) {// 1 // ref.current // ref.current remains constant throughout the life of the component, equivalent to a property in classComponent, // 2 // react source: ref Object is sealed with object.seal (). IsMounted. // When initializing, enter if, false => true; } else {return effect(); // 1. Update => effect() is not executed for the first time, and will only be executed when dependent updates are performed. If there is no dependency deps, it is consistent with the (compoenntDidMount) behavior}}, deps); }; export default useUpdateEffect;Copy the code

data

  • Handwritten hook to achieve react.iamkasong.com/hooks/creat…

  • Handwritten list cloud.tencent.com/developer/a…