navigation

[react] Hooks

[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 Deploy React + Egg +nginx+mysql [React From zero practice 05- background] Gitlab-CI using Docker automated deployment

[source code – Webpack01 – precompiler] AST abstract syntax tree [source code – Webpack02 – Precompiler] Tapable [source code – Webpack03] hand written webpack-compiler simple compilation process [source code] Redux React-redux01 [source] Axios [source] vuex [source -vue01] Data reactive and initialize render [source -vue02] Computed responsive – Initialize, access, Update Procedure [source -vue04] Watch Listening properties – Initialize and update [source -vue04] vue. set and vm.$set [source -vue05] vue.extend

Vue. NextTick and VM.$nextTick

[Deployment 01] Nginx [Deployment 02] Docker deployVue project [Deployment 03] gitlab-CI

[Deep 01] Execution context [Deep 02] Prototype chain [Deep 03] Inheritance [Deep 04] Event loop [Deep 05] Curri Bias function [Deep 06] Function memory [Deep 07] Implicit conversions and operators [Deep 07] Browser caching mechanism (HTTP caching mechanism) [Deep 08] Front-end security [Deep 09] Deep copy [Deep 10] Debounce Throttle [Deep 10] Front-end routing [Deep 12] Front-end modularization [Deep 13] Observer mode Publish subscribe mode Bidirectional data binding [Deep 14] Canvas [Deep 15] webSocket Webpack HTTP and HTTPS CSS- Interview Handwriting Promise algorithms – Find and sort

Front knowledge

(1) Some words

In the case of functional programming, memory functions are deprecated and will be removed in the future. (interface MutableRefObject<T> {current: T; User. Associate = function() {// app.model.user.hasmany (app.model.diary, {foreignKey: 'user_id', targetKey: 'id'})}) legend: imperative: useImperativeHandleCopy the code
  • 2021/01/08 updated

(1) useState

  • UseState Function signature
    • function useState<S>(initialState: S | (() => S)): [S, Dispatch<SetStateAction<S>>]
      • type Dispatch<A> = (value: A) => void
      • type SetStateAction<S> = S | ((prevState: S) => S)
  • It means to sign in person
    • [state, setter] = useState(initialState)
    • initialState
      • (is a value) or (a function that returns a value)
    • setter
      • The setter is a function that does not return a value. The argument to the setter function is a value or a function. The argument to the setter function is the last state, and the return value and the argument type have to be the same
  • The magic of useState !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
    • Setter functions can trigger a re-render
    • State is a separate scope, and rerendering does not redeclare the assignment, but exists in the closure
    • State has an independent value, capture value, for each rendering
      • Extension: Each render, (state, event listener) etc are independent, that is, only belong to that render

(2) useEffect

  • UseEffect Function signature
    • function useEffect(effect: EffectCallback, deps? : DependencyList): void
      • type EffectCallback = () => (void | (() => void | undefined))
      • type DependencyList = ReadonlyArray<any>
  • It means to sign in person
    • useEffect(() => { return Cleanup(){… }}, […]. )
    • The first argument: is a function that can also return a Cleanup function
    • The second argument: is an array of dependencies
  • The magic of useEffect !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
    • UseEffect first argument function
      • UseEffect is executed after rendering is complete and therefore does not block rendering
        • Execute before rendering (UseLayoutEffect synchronization hook )
          • What about when the render performs some tasks? You can use useLayoutEffect
        • Execute after rendering (UseEffecct asynchronous hook )
          • UseEffect is executed after rendering and does not block rendering, improving performance
      • Before useEffect executes, the last useEffect Cleanup function (if any) is executed.
        • UseEffect is executed only once
        • UseEffect does not execute the Cleanup function the first time it is executed
        • Run the last useEffect Cleanup function of the component when it is uninstalled
      • When the component is destroyed, the last useEffect Cleanup function is executed
    • The second argument to useEffect – the dependency array
      • The dependency array controls whether the useEffect() hook function is fired
      • UseEffect is equivalent to (componentDidMount) (componentDidUpdate) (componentWillUnmount) three life cycles
        • Simulate componentDidMount(only during the mount phase)
          • useEffect(()=>{}, [])
          • When the dependent array is empty, the Cleanup function, if there is one, will not be executed after the first time and will not be executed until the component is destroyed
        • Simulate componentDidUpate(not executed in mount phase, executed in update phase)
          • With a flag bit, isDidUpdate defaults to false, then useEffect is not executed the first time, set it to true, and is executed the second time
          • UseEffect (() => {if(isDidUpdate){// compenntDidUpdate phase execution code}; setIsDidUpdate(true) })
      • How are dependent arrays aligned? , mainly through object. is to do the comparison
        • So, if you rely on the array member being a reference type (array, object, function), you need to use useMemo() and useCallbak()
        • UseMemo (() => object, [dependent array])
        • UseCallback (fn, [dependency array])
        • UseMemo is used to cache any type of data, including functions
        • namelyUseMemo (() => fn, []) = useCallback(fn, [])
      • UseEffect (() -> {setters ()}, [dependencies]);
        • There is no need to manually memoizaton the setter functions through useMemo or useCallback because react internally memoizaton the setter functions each time
    • One more note
      • UseEffect (async() => {}, [dependency]
      • This is because: the useEffect signature indicates that the first argument function either returns no value or returns a Cleanup function, while the aysnc function returns a promise object. This is strongly not recommended

(1-2) Summarize useState and useEffect

  • UseState and useEffect are added to the Hook (The list)
  • UseEffect will also be added in a (The queue), which is useEffect’s first argument function. After rendering is complete, the Effect callback function in the queue is called in turn
  • On why hooks must be placed in component headers? Can’t be used in a loop, nested, conditional statement?
    • This is primarily to ensure that the order in which you call the hooks is consistent each time the function component is executed
    • Because looping, nesting, and conditional statements are dynamic statements, it is possible that each function component may not call hooks in the same order, invalidating the data recorded in the linked list

(3) The magic useRef

  • const refContainer = useRef(initialValue);
    • Function signature:function useRef<T>(initialValue: T|null): RefObject<T>;
    • If current is a constant, this is requiredConst countRef = useRef | null > < type (null)
  • Related methods:
    • useImperativeHandle
      • UseImperativeHandle (ref, createHandle, [deps]) allows you to customize the instance value exposed to the parent component when using ref
      • UseImperative should be used in conjunction with React. ForwardRef and should be avoided as much as possible
    • React.forwardRef
      • ForwardRef (fnComponent(props, ref)) can receive the ref object passed by the parent component and return a React node
  • UseRef returns a (variable ref object), (ref.current) initialized to initialValue
  • The ref object returned remains the same throughout the lifetime of the component, i.e. the same REF object is returned every time it is rendered
  • Pay attention to the point:
    • The ref object returned by useRef, which remains the same throughout the lifetime of the component, is equivalent to an instance attribute in a class component
    • If the value of the ref object is updated, it will not be rerendered
    • Changes in ref.current should be treated as a Side Effect (because it will affect the next render), so the current property should not be updated during the Render phase
    • Re.current cannot be used as a dependency on other hooks (useMemo, useCallback, useEffect)
    • UseState returns a snapshot of the state each time, each rendering independent of each other; UseRef is not a snapshot, but a reference to the same object
  • Usage scenarios
    • 1. The binding of the DOM
    • 2. The parent component calls methods in the child component
    • 3. Store arbitrary mutable values, similar to instance attributes in class
    • 4. Use (useRef+setTimeout) to achieve the change of state, immediately get the modified valueNote that if you change the state immediately, and only use setTimeout to get the state, it is not up to date, because you are getting a snapshot of the state, even if the print is delayed
  • UseRef useImperativeHandle, React. ForwardRef integrated case:
import React, { useRef, useState, useEffect, useImperativeHandle } from 'react' interface IGetmessage { getMessage: () => void } const Father = () => { const [count, setCount] = useState(0) const inputRef = useRef<HTMLInputElement>(null) const childRef = useRef<IGetmessage>(null) const CountRef = useRef < number | null > (null) useEffect (() = > {countRef. Current = count / / after each rendering, Const getFocus = () => {inputref.current && inputref.current.focus () console.log('useRef bound DOM node '); } const add = () => {setCount(prevCount => prevCount + 1) console.log(count, 'setState ') Since the callback is not executed, you can use the setTimeout macro task +sueRef to get it. Print after update ') setTimeout(() => {console.log(countref.current, 'this is the latest value of state obtained after setState using (setTimeout+useRef) ')}, 1000) setTimeout(() => {console.log(count, 'note: If you print state in setTimeout directly after setState, it is still not the updated state. }, 1000)} const delayConsole = () => {setTimeout() => {console.log(' do not use useRef delay to print cout, is print snapshot count, >> ', count); >> ', count); }, 3000)} const delayConsoleUseRef = () => {setTimeout(() => {console.log(' save count with useRef, update ref.current after each render, Instead of printing the snapshot, print the count :>> ', countref.current); }, 3000)} const getChildMehod = () = > {childRef. Current & & childRef. Current. GetMessage () / / call subcomponents transmission method} return (< div style={{ background: '#fff', margin: '10px 0', padding: '10px', border: <p style={{margin: '10px', padding: '14px 24px', background: '#e8eaff', border: : 0; '1px solid #345bf9', display: 'inline-block', <div> <input type="text" ref={inputRef} /> <button onClick={getFocus}> </button> </div> <br /> <div style={{ background: '#bcffb7', padding: '10px', margin: '10px 0' }}> <p>count: {count}</p> <button onClick={add}>add</button> &nbsp; <button onClick={delayConsole}> count</button> &nbsp; <button onClick={delayConsoleUseRef}> </button> < div> <br /> <button onClick={getChildMehod}>useRef+useImperativeHandle </button> <Child  ref={childRef} /> </div> ) } const Child = React.forwardRef((props: any, ref: {// reacted.ref () props useImperativeHandle(ref, () => ({// useImperativeHandle() sets the method that allows the child component to be exposed to the parent getMessage: () => {console.log(' print the content of the child component method '); } })) return ( <div style={{ margin: '10px', border: '1px solid red', padding: }}> <p> child </p> </div>)}) export default FatherCopy the code

(4) useReducer, useContext, React.createContext implement a simple redux

  • Reducer function
    • Concept:
      • (state, action) => newState
    • Features:
      • It has to be a pure function
      • Array.prototype.reduce(reducerFunction)
        • The callback functionreducerFunctionIs areducerThe function,Take an old result value, return the new result value and assign to the old result value, and continue the iteration
  • useReducer
    • const [state, dispatch] = useReducer(reducer, initialArg, init);
  • useContext
    • The const Value = useContext(context) parameter is the context instance, and the return value is the current value of the context, that is, the nearest Provider component value
    • useContext(context)The equivalent ofcontext.Consumer
    • useContext(context)The function of:Read the contextThe values of andSubscribe to the context change.At the upper level, you still need context.Provider to provide the context value
  • React.createContext()
    • Const MyContext = React.createconText (defaultValue) Generates a context instance
    • context.Provider
      • < mycontext. Provider value={/* a value */}>
      • Provider component: For the consuming component to subscribe to changes in the context and provide value to the consuming component
      • When a Provider component changes, everything inside the Provider (consuming components) will be rerendered. Value changes are lightly compared with object.is (), so be aware that a value is an Object
      • Note (When the Provider’s value is an object, promote the value cached in the parent node’s state)
    • context.Consumer
      • < myContext.consumer >{value => React element}</ myContext.consumer >
      • Value is the value provided by the most recent Provider. If no Provider exists, the react.createcontext (defaultValue) value is used
  • Implement a redux
import React, { useReducer, useContext } from 'react'; import ReactDOM from 'react-dom'; Const Store = React.createcontext () // initialState => Initialize the reducer state const initialState = { count: => newState const reducer = (state, action) => {switch (action.type) {case 'ADD': return { ... state, count: action.payload } default: return { ... Const Provider = ({children}) => {const [state, dispatch] = useReducer(reducer, initialState) // useReducer return <Store.Provider value={{ state, Dispatch}}> {children} </ store.provider >} // App component const App = () => {const {state, dispatch } = useContext(Store) const { count } = state return <> <p> count: {count}</p> <button onClick={() => dispatch({ type: 'ADD', payload: Count + 1})}>add</button> </>} // mount reactdom. render(<Provider> <App /> </Provider>, document.getElementById('root') );Copy the code

(5) Performance optimization for useCallback, useMemo, React.Memo

  • useCallback
    • useCallback(fn, deps)
      • Fn: Functions that require caching
      • Deps: Dependency array
    • role:
      • If the dependencies do not change, return the cached function directly. Similar to function memory, return the new function only when the dependencies change
      • In summary, useCallback is used to solve (function reference equality)
      • Const memoFn = useCallback(fn, []); useEffect(() => {}; useEffect(() => {}); [memoFn]), useEffect will not re-execute the argument function, because the return from object.is is always true, and memoFn is the same function each time
    • Usage scenarios:
      • If the dependencies on useCallback remain the same, then the function attributes will be re-used. If the dependencies on useCallback remain the same, then the function attributes will be re-used. If the dependencies on useCallback remain the same, then the function attributes will be re-used. This can be used for performance tuning
      • 2. Cache the function when the dependency on useEffect() changes
  • useMemo
    • Function:
      • It is mainly used to cache (object type) data. Values of any type can be used, but the object is used for optimization
      • UseMemo is a superset of useCallback and can cache any type of data
      • UseCallback (f1, [a]) is equal to useMemo(() => f1, [a])
      • useMemo(() => object, [a])
    • Usage scenarios
      • Similar to useCallback
      • UseMemo can cache part of a component, much smaller than React. Memo, but not the entire component!!
      • UseMemo’s finer grained cache – see this blog post for more details
  • React.memo
    • React.memo(functionComponent, areEqual)
      • The first argument: the component that functionComponent needs to cache;
      • The second argument: areEqual cache condition function, returns true to cache, returns false to re-render
    • React.memo() is used for functional components. The default is to make a light comparison of props, and use the second argument function if you want to make further comparisons
React.memo(functionComponent, areEqual) 1. The first parameter is the functionComponet 2 that needs to be cached. The second argument is the comparison rule function areEqual. If it returns true, it will not re-render the function. If it returns false, it will re-render the function. // Parent const Father = () => {const [count1, setCount1] = useState({number: 1 }) const [count2, setCount2] = useState(2) const addCount1 = () => { setCount1(prev => ({ ... prev, number: prev.number + 1 })) } const addCount2 = () => { setCount2(prev => prev + 1) } return <MemoryChild count1={count1} AddCount1 = {addCount1} count2 = {count2} addCount2 = {addCount2} / >} / / subcomponents const Child = ({count1, count2 addCount1, addCount2 }) => { return ( <> <p>count1: {count1.number}</p> <p>count2: {count2}</p> <button onClick={addCount1}>add - count1</button> <button onClick={addCount2}>add - count2</button> </> ) }  const areEqual = (prev, next) => { // 1. When count2 changes, the Child is not rerendered // 2. Return prev.count1.number === next.count1.number} const MemoryChild = react.memo (Child, areEqual) // ------------- React.memo(functionComponent, areEqual)Copy the code
  • Performance optimization for useCallback, useMemo, react. Memo
const Father = () => { const [count, setCount] = useState(0) const [number, setNumber] = useState(1) const add = () => { setCount(prevCount => prevCount + 1) } const memoryAdd = useCallback(add, []) const obj = { age: 20 } const memoryObj = useMemo(() => obj, []) return (<> <button onClick={() => setNumber(number => number + 1)} Callback </Child> <NotMemoryFnChild count={count} add={add} /> <MemoryFnChild count={count} memoryAdd={memoryAdd} /> <NotMemoryObjChild obj={obj} /> <MemoryObjChild memoryObj={memoryObj} /> </> ) } const Child = () => { return ( <div style={{ margin: '10px', border: '1px solid red', padding: '4px'}}> <div> Pure function component - parent component re-render, <div> <div>{math.random ()}</div> </div>)} const NotMemoryFnChild = react.memo (({count, memoryAdd }) => { return ( <div style={{ margin: '10px', border: '1px solid red', padding: }}> <div> < p style = "box-sizing: border-left; color: RGB (62, 62, 62); <div> <div>{math.random ()}</div> </div>)}) const MemoryFnChild = react.memo (({count, add }) => { return ( <div style={{ margin: '10px', border: '1px solid red', padding: '10px', background: 'yellow'}} > < div > with useCallback () function of cache subcomponents props, and React. Comparing shallow memo and props hasn't changed, Children do not rerender </div> <div>{math.random ()}</div> </div>)}) const NotMemoryObjChild = React.memo(({obj}) => {return (<div style={{ margin: '10px', border: '1px solid red', padding: }}> <div> Don't use useMemo() to cache the props attribute, even if you do a light comparison with react.memo (), because there are props, each time it's a new object, so the light comparison results are props. <div>{math.random ()}</div> </div>)}) const MemoryObjChild = react.memo (({memoryObj}) => { console.log(memoryObj, 'memoryObj'); return ( <div style={{ margin: '10px', border: '1px solid red', padding: '10px', background: 'yellow'}}> <div> use useMemo() to cache the props attribute, and make a light comparison with the props attribute in react.memo (). Child components don't update < / div > https://juejin.im/editor/drafts/6882614048181993479 < div > {Math. The random ()} < / div > < / div >)})Copy the code

(6) Chain judgment operator? .

  • ? .In a chained call, we determine if the object on the left is null or undefined, and yes, we don’t go down, we return undefined
  • The chain judgment operator can be used in three ways
    • obj? .attributeObject properties
    • obj? [attribute]Object properties
    • function? .(params)Function/method calls
const b = {fn: x} function x(params) {console.log(params) } b.fn? (1111111) // if fn exists, call fn(1111111) // if fn does not exist, return undefined // ---------- chain judgment operator, determine whether the left object is null or undefined, if yes, return undefined, No, just continue to judgeCopy the code

(7) The difference between === and object.is

  • = = =
    • ( The data type) and (value(It’s all the same= = =equal
    • Objects are strictly equal only to themselvesEven if two object properties are exactly the same, they are not equal
  • Object.is
    • Similar to the strict equality operator
  • The problem
    • When comparing dependencies of hooks such as useEffect, we use object.is (), so when dependencies are of Object (Object, array, function, etc.) types, they are always unequal and meaningless
  • Solution:
    • If it’s a function, you can use ituseCallbackDo caching, so that when useEffect’s dependency has a function, the function is fixed every time
NaN === NaN // false object.is (NaN, NaN) // True +0 === -0 // True object.is (+0, -0) // falseCopy the code

(8) Array – non-numeric keys

  • For in can traverse (numeric keys) and (non-numeric keys) of a number group
  • Therefore, it is not recommended to use the for in loop to iterate through the number group, because it will iterate through the non-numeric keys. Other loops such as for and forEach will only iterate through the numeric keys
  • For in can traverse groups and objects
const arr = [1, 2, 3]
arr.four = 4
arr.five = 5
// arr =>  [1, 2, 3, four: 4, five: 5]


for(let i in arr) { 
  console.log(i) 
}
// 1 2 3 four five
Copy the code

(9) pseudo class: the NTH – child

Selected before 5 -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- : NTH - child (n + 5) select the fifth element at the back of the children -- -- -- -- -- -- -- -- -- -- - : NTH - child (n + 5) to select 5-10 children ---------------------- :nth-child(n+5):nth-child(-n+9)Copy the code

(10) Package of echarts in react-hooks

  • Encapsulate the functionality to be implemented

    • Pass the parameters:
      • options: objectEcharts Data configuration object
      • isResize: booleanWhether to adapt to window changes
      • showLoading: booleanWhether to display the loading data animation
      • events: objectEvent object that can transmit events that need to be listened on
        • key: Indicates the name of the event
        • value: is a callback function, and the callback parameter isEvents objectandEcharts instance
      • wrapStyle: objectPassed in style
      • The className: stringYou can specifically pass in the class name
  • The related properties and events of the echarts that are needed

    • echarts
      • echarts.init(dom, theme, opts)
      • echarts.getInstanceByDom(dom)
    • echartsInstance: echarts.init() generated instance
      • EchartsInstance. SetOption () : echarts icon items of data
      • Echartsinstance. on: Handler for binding events
      • Echartsinstance.resize () : Resize the chart. This is called manually when the container size changes
      • EchartsInstance. ShowLoading () : the animation display
      • EchartsInstance. HideLoading () : closed loading animation
      • Echartsinstance.clear (): Clear the current instance, removing all components and graphs from the instance
  • code

Import React, {useEffect, useRef, useState} from 'React' import echarts from 'echarts' interface Ioption {option: IAny; // Configure object wrapStyle? : IAny; / / style className? : string; // Create a custom class with a unique prefix, theme? : string; / / theme events? : IAny; // The configuration object of the event, the key event name, the value event callback, the callback has two parameters: events and echarts instance isResize? : boolean; // Do you want to adapt to window changes by showLoading? : boolean; Loading} interface IAny {[propName: string]: any} const HocEcharts = ({option, // configure object wrapStyle = {width: '400px', height: '400px', background: '# FFF '}, // style className,// custom class, to not affect the global, // If loading isResize = true, // if loading isResize = true, // if loading isResize = true, // if loading isResize = true, Key event name, the value of the callback, the callback events and echarts instance two parameters: attach Ioption) = > {const ref = useRef < HTMLDivElement | any > (null) let the instance: Echarts.echarts // getInstance Creates or gets an instance const getInstance = async () => {instance = await echarts.getInstanceByDom(ref.current) || await echarts.init(ref.current, Theme) instance.clear() // Clear the instance} // setOption setting configuration item const setOption = async () => {showLoading && Instance.showloading ('default') // loading animation to start await new Promise(resolve => {setTimeout(() => {instance && Resolve ()}, instance.setoption (option) 1000)}) showLoading && instance.hideloading () // loading animation start} const bindEvent = () => {if (instance && events) {for (let i in events) { instance.on(i, events[i].query, (e: any) => events[i].callback(e, }}} const init = async () => {await getInstance() // Generate or get await setOption() // Set echarts configuration item } const resizeEcharts = () => {instance && instance.resize()} useEffect(() => {init()}, []) useEffect(() => {// listen for window changes, Echarts adaptive if (isResize) {window.addeventListener ('resize', ResizeEcharts) return () = > window. The removeEventListener (' resize, resizeEcharts) / / removing listen}}, []) return ( <div ref={ref} style={wrapStyle} className={className} /> ) } export default HocEchartsCopy the code
<HocEcharts option={barOption2} className="custom-echarts-bar" theme={theme} isResize={true} showLoading={true} events={Events} />Copy the code

(b) custom hooks

(1) usePrevious

  • Used to get the previous value
import { useEffect, useRef } from 'react';

interface IusePrevious {
  <T>(state: T): T
}

export const usePrevious: IusePrevious = (state) => {
  const ref = useRef(state)
  useEffect(() => {
    ref.current = state
  })
  return ref.current
}
Copy the code

(2) useModal

  • const { CustomModal, toggle } = useModal(title)
  • Parameters: The title passed to Modoal, and the content is taken from children
  • Return value: returns a CustomModal component, and toggles the display hidden callback
  • define
Definition: // Both useModal and CustomModal returned received props import React, { useState } from 'react' import { Modal } from 'antd' interface IformInstance { submit: } interface ICustomModalProps {formInstance? : IformInstance; children: any; } const useModal = (title: string) => { const [visible, setVisible] = useState(false) const toggle = () => { setVisible(prevVisible => ! prevVisible) } const CustomModal = (props: ICustomModalProps) => {// Return the CustomModal component to the business side using const {formInstance, Children} = props const handleOk = () => {formInstance && formInstance. Submit () // If child is an instance of form, submit the form, } const handleCancel = () => {setVisible(false)} return (<Modal title={title}) visible={visible} onOk={handleOk} onCancel={handleCancel} > {children} </Modal> ) } return { CustomModal, toggle } } export { useModal }Copy the code
  • Definition:
Import {usePrevious} from '@/utils/hooks/use-previous' import {useModal} from '@/utils/hooks/use-modal' // -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- the introduction of the import {Button, the Form, the Input} from 'antd import React, { useState } from 'react' const CustomHooks = () => { const { CustomModal, toggle } = useModal('USEMODAL') const swtichModal = () => { toggle() } // -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- call return (< CustomModal formInstance={form}> <Form name="basic" initialValues={{ remember: True}} form={form} > < form. Item label="Username" name=" Username" > <Input /> </ form. Item> </ form > <div> </CustomModal> ) } export default CustomHooksCopy the code

(3) useFetch

  • Parameter: FETCH fetchParmas
  • Return value: data doFetch loading params
useFetch ---- import { useState, useEffect, useCallback } from "react"; type Tfetch = (... rest: any[]) => any; Interface IfnParams {current? : number; pageSize? : number; total? : number; [propNmae: string]: any; } interface Iconverter { (data: any): any; } type TuseFetch = (fetch: Tfetch, fetchParams? : IfnParams, // request function parameter isInitRun? : Boolean | 'initRun' | 'initNotRun', / / initialization time, whether to perform the requested function, Boolean, and two strings' initRun ' 'initNotRun converter? : Iconverter, // convert function) => ({data: any; doFetch: Tfetch; loading: boolean; params: IfnParams; }); const useFetch: TuseFetch = ( fetch, fetchParams = { current: 1, pageSize: 8, total: 10,}, isInitRun = true, // when initialized, whether to execute the request function, accept Boolean, and two strings 'initRun' 'initNotRun', Default value true Converter = (data) => data) => {const [params, setParams] = useState(() => ({current: 1, pageSize: 8,... fetchParams })); const [data, setData] = useState<any>(null); const [loading, setLoading] = useState(false); Const [isGoRun, setIsGoRun] = useState(isInitRun) //!!!!! We need to replace the passed parameter isInitRun with state, because the passed parameter isInitRun is always the same, the request function fech is never executed, and we're going to call doFetch, Const memoryFetch = useCallback(fetch, []); const memoryconverter = useCallback(converter, []); // const memoryParams = useMemo(() => params, [params]) // 1. Params is a reference type and does not need to be cached in useEffect because the state itself is cached. But: if it is a constant aaaaa is a reference type, the useEffect useMemo must be made in the cache, the Object, is () is always false, the infinite loop / / const aaaaa = {a: 1111} // useEffect(() => console.log(aaaaa), [aaaaa]) useEffect(() => { if ((typeof isGoRun === 'boolean' && ! isGoRun) || isGoRun === 'initNotRun') { return } const fetchData = async () => { setLoading(true); try { const res = await memoryFetch(params); setLoading(false); if (res.data) { setData(() => memoryconverter(res.data)); } } catch (err) { setLoading(false); console.error(err); }}; fetchData(); }, [memoryFetch, params, memoryconverter, isInitRun, isGoRun]); Const doFetch = (fetchParams: IfnParams): SetParams (prevState => ({prevState => ({... prevState, ... fetchParams})); }; return { data, doFetch, loading, params }; // return // data: data // doFetch: request function // loading: such as loading for table // params: such as paging parameters for table}; export { useFetch };Copy the code

(4) useOnce

  • Parameter: delay How many seconds to change the delay, if not passed, will change on the next render
  • Returns: once Boolean
import { useState, useEffect } from 'react'; const useOnce = (delay? : number) => { const [once, setOnce] = useState(false) useEffect(() => { delay ? setTimeout(() => { setOnce(() => true) }, delay) : setOnce(() => true) }, [delay]) return once } export { useOnce }Copy the code

(5) useViewport

  • Features: Real-time access to HTML width and height
  • Parameters:
    • DoSomething: Function that needs to be executed when the viewport changes
  • The return value:
    • width
    • height
  • Optimization: If you can further optimize, implement one yourself
  • UseViewport definition
------ import {useEffect, useReducer, useCallback } from 'react' interface IViewportState { width? : number; height? : number; } interface IViewportActionn { type: string; payload: any; } // constant const actionType = { CHANGE_VIEWPORT: 'CHANGE_VIEWPORT' } // reducer const viewPortReducer = (state: IViewportState, action: IViewportActionn) => { switch (action.type) { case actionType.CHANGE_VIEWPORT: return { ... state, width: action.payload.width, height: action.payload.height, } default: return { ... state } } } // initialState const initialState: IViewportState = { width: 0, height: 0, } / / -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- reducer version -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- useViewport / * * * * @ desc high real-time access to the viewport width * @ param {function} */ export const useViewport = (doSomething? : () => void) => { const [state, dispatch] = useReducer(viewPortReducer, initialState) const changeViewport = () => { const HTML_DOM = document.documentElement const width = HTML_DOM.clientWidth const height = HTML_DOM.clientHeight dispatch({ type: actionType.CHANGE_VIEWPORT, payload: { width, height } }) if (doSomething) { doSomething() } } const memoryChangeViewPort = useCallback(changeViewport, []) useEffect(() => { memoryChangeViewPort() window.addEventListener('resize', memoryChangeViewPort, Return () => {window.addeventListener ('resize', memoryChangeViewPort, false)}}, [memoryChangeViewPort]) return { width: state.width, height: state.height, }} / / -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- the state version -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- / / export const useViewport = () = > {/ / const HTML_DOM = document.documentElement // const [width, setWidth] = React.useState(HTML_DOM.clientWidth) // const [height, setHeight] = useState(HTML_DOM.clientHeight) // const changeWindowSize = () => { // const HTML_DOM_CURRENT = document.documentElement // setWidth(v => HTML_DOM_CURRENT.clientWidth) // setHeight(v => HTML_DOM_CURRENT.clientHeight)  // } // useEffect(() => { // window.addEventListener('resize', changeWindowSize, false) // return () => { // window.removeEventListener('resize', changeWindowSize, false) // } // }, []) // return { width, height } // }Copy the code
  • UseViewport use
UseViewport custom hooks use ------ import React, { useRef } from 'react' import { useViewport } from '@/utils/hooks/use-viewport' import './smart-viewport.scss' const SmartViewport = () => { const ref = useRef<HTMLDivElement>(null) const timerRef = useRef<any>(0) const { width, height } = useViewport(doAnimate) const debounce = () => { if (timerRef.current) { window.clearTimeout(timerRef.current)  } timerRef.current = window.setTimeout(() => { if( ref.current) { ref.current.style.display = 'none' } }, 2000) } function doAnimate() { if (ref.current) { ref.current.style.display = 'block' } debounce() } return ( <div className="smart-viewport" ref={ref}> <span>wdith: {`${width}px`}</span> <span>height: {`${height}px`}</span> </div> ) } export default SmartViewportCopy the code

(6) useDebounce

  • Note: You cannot assign a value to the Timer using a closure variable, otherwise other state updates will re-execute the debounce function and the timer will be re-assigned to its original value
  • UseDebounce definition
import { useRef } from "react"; interface IuseDebounce { (fn: Ifn, delay? : number, immediate? : boolean): IClosure; } interface Ifn { (... rest: any[]): any; } interface IClosure { (e: any, ... rest: any[]): any; } /** * @desc debounce * @param {function} fn Function to be executed * @param {number} delay Delay period * @param {Boolean} immediate */ export const useDebounce: IuseDebounce = (fn: any, delay = 1000, immediate = false ) => { const refTimer = useRef(0); Return (e,... rest) => { if (immediate && ! refTimer.current) { fn.call(rest); refTimer.current = 1; // The return function will not be entered until the first time; } if (reftimer.current) {window.clearTimeout(reftimer.current); } refTimer.current = window.setTimeout(() => { fn.call(rest); }, delay); // cancel debounce // useDebounce. Cancel = function() {// if(reftimer.current) {// window.clearTimeout(reftimer.current) // } / /}}; }; / / -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- variable timer version -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- / / questions: // How to verify: UseDebounce was reexecuted when another state was updated in UseDebounce: // export const useDebounce: IuseDebounce = (fn:) any, delay = 1000, immediate = false) => { // let timer = 0; // return (e, ... rest) => { // if (immediate && ! timer) { // fn.call(rest); // timer = 1; // return; // } // if (timer) { // window.clearTimeout(timer); // } // timer = window.setTimeout(() => { // fn.call(rest); // }, delay); / /}; / /};Copy the code
  • UseDebounce call
import React, { useEffect, useState, useRef } from 'react' import { useDebounce } from '@/utils/hooks/use-debounce' const UseDebounce = () => { const [count,  setCount] = useState(0) const refInterval = useRef(0) const doSomething = () => { console.log('debounce'); } useEffect(() => { refInterval.current = window.setInterval(() => { setCount(v => v + 1) }, 1000) return () => window.clearInterval(refInterval.current) }, []) return ( <div style={{ background: '#fff', margin: '10px 0', padding: '10px', border: '1px solid black' }} > <br/> <p>useDebounce</p><br/> <div> {count}</div><br/> <button onClick={useDebounce(doSomething, 1000, false)}> Click test - debounce function to see console </button> </div>)} export default UseDebounceCopy the code

(7) useThrottle

  • UseThrottle definition
import { useRef } from 'react' interface IuseThrottle { (fn: Ifn, delay: number): IClosure; } interface Ifn { (... rest: any[]): any } interface IClosure { (e: any, ... rest: any[]): any } export const useThrottle: IuseThrottle = (fn, delay) => { const refGoRun = useRef(true) const refTimer = useRef(1) return (e, ... args) => { if (! refGoRun.current) { return } refGoRun.current = false refTimer.current = window.setTimeout(() => { fn.call(args) Refgorun-current = true // Window.clearTimeout (reftimer.current) // Clear the timer}, delay)}}Copy the code
  • UseThrottle use
import React, { useEffect, useState, useRef } from 'react' import { useThrottle } from '@/utils/hooks/use-throttle' const UseThrottle = () => { const [count,  setCount] = useState(0) const refCount = useRef(0) useEffect(() => { refCount.current = window.setInterval(() => setCount(count => count + 1), 1000) return () => window.clearInterval(refCount.current) }, []) const doSomething = () => { console.log('throttle'); } return ( <div style={{ background: '#fff', margin: '10px 0', padding: '10px', border: '1px solid black' }}> <p style={{ margin: '10px', padding: '14px 24px', background: '#fdf2ff', border: '1px solid #e821ff', display: 'inline-block', }}>useThrottle</p> <br /> <br /> <div>{count}</div> <br /> <br /> <button onClick={useThrottle(doSomething, </button> </div>)} export default UseThrottleCopy the code

(8) useIntersectionObserver implements lazy image loading

  • useIntersectionObserver(doms, option)
  • Pre-knowledge:
  • const io = new IntersectionObserver(callback, option)
  • parameter
    • callback: callback function for visibility changes
      • The callback is typically triggered (twice), entering the viewport and leaving the viewport, i.e. starting to be visible and starting to be invisible twice
      • The callback,parameter), is a (IntersectionObserverEntryIs composed of objectsAn array of )
        • If the visibility of two observables changes, the array has two members
        • IntersectionObserverEntry six attributes
          • Time: indicates the time when visibility changes. It is a timestamp in milliseconds
          • target:The target element being observed is a DOM node
          • RootBounds: Information about the rectangular bounds of the root element. This is the return value of getBoundingClientRect(). No root element returns null
          • BoundingClientRect: Information about the rectangular area of the target element
          • IntersectionRect: information about the intersection area between the target element and the root element
          • intersectionRatio:Target element visible than columns, intersectionRect/boundingClientRect, fully visible = 1, totally invisible < = 0
    • Option: Configures the parameter object
      • Threshold: An array with a default value [0] that triggers a callback when the cross ratio reaches 0
      • Root: specifies the root element
      • RootMargin: Used to expand or reduce the size of the rectangle rootBounds, thus affecting the size of intersectionRect
  • The return value
    • Observer instance
    • Observe: io.observe(document.getelementById (‘example’));
    • Stop viewing: IO. Unobserve (element);
    • Close the observer: io.disconnect();
  • compatibility
    • A Polify library for improved compatibility
    • intersection-observer
  • Note:
    • The IntersectionObserver API is asynchronous and does not trigger synchronously with the scrolling of the target element
  • conclusion:
    • Option. root: refers to (container node)
    • Target: the target in IO. Observer (target) refers to the object to be observed.
    • The target node must be a child node of the container node.
    • When there is an intersection between the target node and the container node, a callback is triggered, both on entry and off, and multiple cross ratios can be specified with option.threshold
  • UseIntersectionObserver definition
import { useRef, useEffect } from 'react'; type TuseIntersectionObserver = (doms: any[], option: IOption) => void interface IOption { root? : any; rootMargin? : string; threshold? : number[]; } export const useIntersectionObserver: TuseIntersectionObserver = (doms, option) => { const refIo = useRef<any>(null) useEffect(() => { if (! doms.length) return; Refio. current = new IntersectionObserver((entrys) => {entrys.foreach ((item) => {if (item.IntersectionRatio > 0) {// intersectionRadio intersectionRatio, Item.target.setattribute (' SRC ', '${item.target.getattribute ('data-src')} ') // entry.target = DOM}})}, Option) doms. ForEach (imageItem => refio.current.observe (imageItem)) return () => Refio.current.disconnect () // close the observer}) // Note: there is no need to rely on the array, because img's first render data may also be obtained asynchronously. Each item can contain SRC and data-src}Copy the code
  • UseIntersectionObserver use
import React, { useEffect, useState, useRef } from 'react' import './test-hooks.scss' import { useIntersectionObserver } from '@/utils/hooks/use-intersectionObserver' interface IimagesData { tempSrc: string; src: string; } const imagesData = [{ tempSrc: require('@/assets/iamges/lazy-temp.png'), src: 'https://cdn.seovx.com/?mom=302' }, { tempSrc: require('@/assets/iamges/lazy-temp.png'), src: 'https://cdn.seovx.com/d/?mom=302' }, { tempSrc: require('@/assets/iamges/lazy-temp.png'), src: 'https://cdn.seovx.com/ha/?mom=302' }] const UseIntersectionObserver = () => { const [images, setImages] = useState<IimagesData[]>([]) const refImg = useRef<any[]>([]) useIntersectionObserver(refImg.current, { root: document.getElementById('use-observer-root'), rootMargin: '0px', threshold: [0] }) useEffect(() => { setTimeout(() => { setImages(() => imagesData) }, 500) }, []) / / -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- no hooks of IntersectionObserver -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- useEffect (() = > {/ / not hooks IntersectionObserver const io = new IntersectionObserver(([entry]) => { console.log(entry); }, { root: document.getElementById('observer-root'), rootMargin: '10px', threshold: [0] }) const target = document.getElementById('observer-target') if (target) { io.observe(target) } return () => { } }, []) const renderImages = (item: IimagesData, index: number) => { return ( <img src={item.tempSrc} data-src={item.src} alt={"images"} key={index + +new Date()} ref={el => refImg.current[index] = el} /> ) } return ( <div style={{ background: '#fff', margin: '10px 0', padding: '10px', border: '1px solid black' }}> <p style={{ margin: '10px', padding: '14px 24px', background: '#edfffb', border: '1px solid #00b792', display: 'inline-block',}} > useIntersectionObserver </p> <br /><br /><p> </p><br />< div id=" obSERVER_root "> <div id=" observer_root "> The < div > this is IntersectionObserver specify the root node of the DOM - green < / div > < div > < p > fill < / p > < br / > < br / > < br / > < p > fill < / p > < br / > < br / > < br / > Filling < / p > < p > < br / > < br / > < br / > < p > fill < / p > < br / > < br / > < br / > < p > fill < / p > < br / > < br / > < br / > < / div > < div id = "observer - target" > <p> This is the node observed by the IntersectionObserver, DOM - red </p><br /><br />< p> This is the node observed by the observer, DOM - red </p><br /><br /> <p> This is the node observed by the IntersectionObserver, DOM - red </p><br /><br />< p> This is the node observed by the observer, DOM - red </p><br /><br /> <p> This is the node observed by the IntersectionObserver, DOM - red </p><br /><br />< p> This is the node observed by the observer, DOM - red </p><br /><br /> <p> This is the node observed by the IntersectionObserver, DOM - red </p><br /><br />< p> This is the node observed by the observer, DOM - red </p><br /><br /> <p> This is the node observed by the IntersectionObserver, DOM - red </p><br /><br />< /div> <div>. Filling < / p > < p > < br / > < br / > < br / > < p > fill < / p > < br / > < br / > < br / > < p > fill < / p > < br / > < br / > < br / > < p > fill < / p > < br / > < br / > < br / > Filling < / p > < p > < br / > < br / > < br / > < / div > < / div > < br / > < br / > < br / > < br / > < br / > < div > IntersectionObserver images lazy loading applications < / div > < br / > < div id = "use - the observer - root" > < div > < p > fill < / p > < br / > < br / > < br / > < p > fill < / p > < br / > < br / > < br / > < p > fill < / p > < br / > < br / > < br / > Filling < / p > < p > < br / > < br / > < br / > < p > fill < / p > < br / > < br / > < br / > < p > fill < / p > < br / > < br / > < br / > < p > fill < / p > < br / > < br / > < br / > Filling < / p > < p > < br / > < br / > < br / > < / div > {images. The map (renderImages)} < div > < p > fill < / p > < br / > < br / > < br / > < p > fill < / p > < br / > < br Filled / > < br / > < p > < / p > < br / > < br / > < br / > < p > fill < / p > < br / > < br / > < br / > < p > fill < / p > < br / > < br / > < br / > < p > fill < / p > < br / > < br / > < br / >  </div> </div> </div> ) } export default UseIntersectionObserverCopy the code

(9) useInputBind

  • So we’re just looking atvalueonChageBecause of the properties that all inputs have
  • Not to joinonSearchbecauseOnly input.search has it, and you need to consider the smallest granularity of encapsulation, and then use function composition to achieve complex functionality
import { useState } from 'react'

type TUseBind = () => ({
  value: string,
  onChange:  (e: React.ChangeEvent<HTMLInputElement>) => void
})

const useInputBind: TUseBind = () => {
  const [value, setValue] = useState('')

  const onChange = (e: React.ChangeEvent<HTMLInputElement>) => {
    setValue(() => e.target.value)
  }

  return {
    value,
    onChange,
  }
}

export {
  useInputBind
}

Copy the code

Program source code

  • Program source code
  • Deployment effect Preview address

data

  • UseCallback () well written juejin.cn/post/684490…
  • UseCallback useMemo React. Memo performance optimization juejin.cn/post/684490…
  • UseReducer + useContext + React.createcontext redux juejin.cn/post/684490…
  • UseCallback & useMemo juejin.cn/post/684490…
  • USES details segmentfault.com/a/119000002 useRef…
  • You don’t know useRef zhuanlan.zhihu.com/p/105276393
  • Custom hooks juejin.cn/post/684490…
  • UseModal blog.csdn.net/jx950915/ar…
  • UseDebounce: juejin.cn/post/684490…
  • UseIntersectionObserver juejin. Cn/post / 686362…