关于react.js:react的hooks教程

84次阅读

共计 7031 个字符,预计需要花费 18 分钟才能阅读完成。

这是一篇 react 的 hooks 教程

另外欢送大家拜访我的博客

react hooksclass Component 的区别

  • 写法更加简洁,不再须要写简短的生命周期函数
  • class Componet hoc 的浏览让人看起来不易了解, 在组件之间复用状态逻辑很难.

hooks 应用规定

  • 只能在 函数最外层 调用 Hook。不要在循环、条件判断或者子函数中调用。
  • 只能在React 的函数组件中调用Hook。不要在其余 JavaScript 函数中调用。除了自定义的 hook 以外

react hooks 常见的 api 应用

useState 用法

identity: const [state, setState] = useState(initialState)

useState的作用是在 react 函数组件中增加 state 的 hook。

    import React, {useState} from 'react';
    function Count() {
        /*
            - count <=> this.state.count, setCount <=> this.setState
            - setCount 反对两种写法 setCount(count + 1) or setCount(preState => preState + 1)
            第一种写法是一种异步机制,会将短时间内的多个 setCount 合并成一个办法,第二种写法是为了不应用第一种的合并机制。*/
        const [count, setCount] = useState(0);
        return <div onClick={setCount(pre => pre + 1)}>{count}</div>
    }

useEffect 用法

identity: useEffect(callBack:clearCallBack, [deps])

useEffect的作用是在函数组件中执行副作用操作, 等价于在 ComponetDidMount,ComponentDidUpdate, ComponentWillUnmount 三个函数的组合。

    import React, {useState, useEffect} from 'react';
    function Example() {
        /*
            - useEffect 承受一个 callBack 参数和数组参数
            - 数组中的值作为依赖,数组中的值发生变化的时候,callBack 会从新调用。等价于 ComponentDidUpdate
            - callBack 能够 return 一个 clearCallBack, 在组件卸载的时候调用 clearCallBack。等价于 ComponentWillUnmount
            - useEffect 默认会在 render 流程执行完当前,在调用 callBack。等价于 ComponetDidMount
        */
        const [isonline, setIsOnline] = useState(false);
        useEffect(() => {ChatAPI.subscribeToFriendStatus(props.friend.id, handleStatusChange)
            return () => {ChatAPI.unsubscribeFromFriendStatus(props.friend.id, handleStatusChange)
            }
        }, [isonline])
    }

useContext 用法

useContext的作用是承受一个 context 对象,并返回该 context 的以后值。次要用于深层级的组件通信。须要和 React.createContext 配合应用
context 的值由下层组件中距离以后组件最近的 <MyContext.Provider> 的 value prop 决定。另外 value 的值更新会引起调用了 uesContext 的组件从新渲染。

留神调用 useContext 的组件即应用了 React.memo 进行申明,也会从新渲染。因而须要应用 memoization 来进行优化.

    import React, {createContext, Children, useContext, useMemo} from 'react'
    const GlobalContext = React.createContext()

    function Child() {const data = useContext(GlobalContext)
        // memoization 写法
        return useMemo(() => {return <div>{data.name}</div>
        }, [data.name])
    }

    function Parent() {
        return (
            <div>
                <p>Parent</p>
                <GlobalContext.Provider value={{name: 'woyao'}}>
                    <Child />
                </GlobalContext.Provider>
            </div>
        )
    }

useRef 用法

useRef返回一个可变的 ref 对象,ref.current在组件内是一个全局常量。相当于在组件外写了一个全局常量。也就是说每次从新渲染函数组件时,返回的 ref 对象都是同一个。罕用于 拜访 Dom,当做全局变量

拜访 Dom

    import React, {useRef} from 'react'
    function Example() {const inputElement = useRef(null)
        const btnClick = (event) => {inputElement.current.focus()
        }
        return (
            <React.Fragment>
                <input ref={inputElement} />
                <button ref={btn} onClick={btnClick}/>
            </React.Fragment>
        )
    }

当做全局变量

    import React, {useRef, useEffect, useState} from 'react'
    function usePrevious(value) {
        // 每次从新渲染,都会执行 useRef,
        const ref = useRef()
        /*
            - 不设置依赖,每次 reRender 都会从新执行
            - 可能返回上一次渲染之前 value 是什么值
            - 留神是先执行 return, 在执行 useEffect
        */
        useEffect(() => {ref.current = value})
        return ref.current
    }
    function Counter() {const [count, setCount] = useState(0)
        const preCount = usePrevious(count)
        return (
            <div>
                <p>previous: {preCount} </p>
                <p>now: {count}</p>
                <button onClick={() => setCount(count + 1)}>click</button>
            </div>
        )
    }

useImperativeHandle 用法

identity: useImperativeHandle(ref, createHandle, [deps])

useImperativeHandle能够让你在应用 ref 时自定义裸露给父组件的实例值, 与 forwardRef 一起应用. 让你可能父组件调用子组件的办法.

    import React, {useRef, useImperativeHandle, forwardRef} from 'react'
    function MyInput(props, ref) {const inputRef = useRef()
        const childFunc = () => {console.log('hh')
        }
        /*
            ref: ref 实例
            createHandle: 给 ref 实例绑上办法
            [dps]: 当 deps 发生变化的时候, createHandle 从新执行
        */
        useImperativeHandle(ref, () => ({focus: () => {inputRef.current.focus()
            },
            childFunc
        }))
        return <input ref={inputRef} />
    }

        MyInput = forwardRef(MyInput)

        function App() {const myInputCoponent = useRef()

            return (
                <>
                    <MyInput ref={myInputCoponent} />
                    <button onClick={() => { myInputCoponent.current.childFunc() }}> focus now </button>
                </>
            )
        }

useReducer 用法

identity: const [state, dispatch] = useReducer(reducer, initialArg, init)

useReducer是 useState 的代替计划,承受一个形如 (state, action) => newState 的 reducer, 并返回
以后的 state 以及其配套的 dispatch 办法。

    const initialState = {count: 0};

    function reducer(state, action) {switch (action.type) {
            case 'increment':
                return {count: state.count + 1};
            case 'decrement':
                return {count: state.count - 1};
            default:
                throw new Error();}
    }

    function Counter() {const [state, dispatch] = useReducer(reducer, initialState);
        return (
            <>
                Count: {state.count}
                <button onClick={() => dispatch({type: 'decrement'})}>-</button>
                <button onClick={() => dispatch({type: 'increment'})}>+</button>
            </>
        );
    }

useMemo 用法

identity: const memoizedValue = useMemo(() => computeExpensiveValue(a, b), [a, b])
newRenderTemplate = useMemo(() => renderTemplate(), [deps])

useMemo是缩小组件渲染次数,优化组件性能的 hook, 传入 useMemo 的函数会在渲染期间执行,请不要再函数
外部执行与渲染无关的操作,其实也就是只有依赖项发生变化才会生成新的 memoizedValue。这样就缩小了不必要的
渲染。个别用在组件中进行解耦操作,与这个逻辑渲染相干的逻辑发生变化就从新渲染,而不相干的就不会从新渲染。
大白话就是有一个 count 逻辑相干的渲染,还有一个和 name 相干的逻辑渲染。不要因为 name 的 state 属性变动导致
count 的渲染函数也从新执行。

    import React, {useState, useMemo} from 'react'
    const log = console.log.bind(console)

    function Child(props) {log('child render')
        const [count, SetCount] = useState(0)
        const renderCountTemplate = (count) => {console.log('count render')
            return <div>{count}</div>
        }
        return (
            <div>
                child, {props.name}
                {/*
                    - 应用 useMemo 避免了不必要的渲染更新,不会因为与以后父组件的 props 发生变化就会从新对 renderCountTemplate 进行执行。*/}
                {useMemo(() => renderCountTemplate(count), [count])}
            </div>
        )
    }
    // Child 组件优化:组件内 prop,state 的值发生变化才会从新渲染。避免了父组件的更新,子组件也进行不必要的更新
    Child = React.memo(Child)


    function App() {log('parent render')
        const [name, SetName] = useState('')
        return (
            <>
                <div>parent, {name}</div>
                <input onChange={(event) => SetName(event.target.value)} />
                <Child name={name} />
            </>
        )
    }

useCallback 用法

identity: const memoizedCallback = useCallback(() => { doSomething(a, b); }, [a, b]);

useCallback是缩小组件渲染次数,优化组件性能的 hook, 与 useMemo 相似。回调函数仅在某个依赖项扭转时才会更新
罕用在对事件函数中匿名函数的解决。当然能用到 useMemo 的中央,useCallback 也能够用到。

    import React, {useCallback, useState} from 'react'

    const [count, setCount] = useState(0)
    function Counter() {
        <!--
            - 避免了每次 count 发生变化,导致的从新渲染,都须要从新生成一个新的匿名函数
            - 也就是说 () => {setCount(count + 1)} 这个匿名函数不须要再从新的内存空间中创立
         -->
        return <div onClick={useCallback(() => setCount(count+1), [])}>{count}</div>
    }

useLayoutEffect 用法

useLayoutEffect 官网解释说它会在所有的 DOM 变更之后同步调用 effect,可能 useEffect 是在所有的 DOM 变更之后异步调用 effect 吧.

  • 能够应用它来读取 DOM 布局并同步触发重渲染
  • 在浏览器执行绘制之前,useLayoutEffect外部的更新打算将被同步刷新。
  • 这么说吧,我也不太 useLayoutEffect 的区别。有上面一段代码能够参考一下。预计在应用 useEffect 的时候带来了页面的抖动问题的时候就应用useLayoutEffect。网上的解释:layout 会在组件树构建结束或者刷新结束后同步立即执行。effect 会等其余 js 代码执行结束后执行

    function App() {const [count, setCount] = useState(0);
    
    useEffect(() => {if (count === 0) {const randomNum = 10 + Math.random()*200
        setCount(10 + Math.random()*200);
      }
    }, [count]);
    
    return (<div onClick={() => setCount(0)}>{count}</div>
    );
    }

    成果如下:

    function App() {const [count, setCount] = useState(0);
    
    useLayoutEffect(() => {if (count === 0) {const randomNum = 10 + Math.random()*200
        setCount(10 + Math.random()*200);
      }
    }, [count]);
    
    return (<div onClick={() => setCount(0)}>{count}</div>
    );
    }

    成果如下:

自定义 hook

  • useDidUpdate
  • useGlobalReduxHook

<div id=”useDidUpdate”></div>

// 一个 ComponentDidUpdate 的繁难实现
import {useEffect, useRef} from 'react'

function useDidUpdate(cb, deps=[]) {const didMount = useRef(false)

  useEffect(() => {if (!didMount.current) {
      didMount.current = true
      return
    }
    cb()}, deps)

  return didMount.current
}

<div id=”useGlobalReduxHook”></div>

    // 一个 redux 的繁难实现
    import React, {useContext, useReducer} from 'react'
    const initState = {count: 0}
    const Store = React.createContext(initStore)
    const MapActionReducer = {['ADD'](state, action) {return {...state, count: action.payload}
        }
    }

    const reducer = (initState, action) => {return MapActionReducer[action.type](initState, action)
    }

    function useGlobalReduxHook(Component) {
        // dispatch 触发 state 发生变化,会从新执行渲染
        const [state, dispatch] = useReducer(reducer, Store)
        return (<Store.Provider value={state, dispatch} />
                <Component />
            </Store.Provider>
        )
    }

正文完
 0