美文网首页
[react]24、自定义hooks

[react]24、自定义hooks

作者: 史记_d5da | 来源:发表于2021-12-02 21:37 被阅读0次

    1、自定义Hook的基本使用

    自定义Hook本质上只是一种函数代码逻辑的抽取,严格意义上来说,它本身并不算React的特性。

    function CustomHookLifeDemo01() {
        useLoggingLifeCycle('CustomHookLifeDemo01')
        return (
            <div>
                <h2>CustomHookLifeDemo01</h2>
                <Home />
                <Profile />
            </div>
        )
    }
    // 必须以use开头的function才能使用 useEffect函数
    function useLoggingLifeCycle(name) {
        useEffect(() => {
            console.log(`${name}组件被创建`);
            return () => {
                console.log(`${name}组件被销毁掉`);
            }
        })
    }
    

    2、自定义useContext

    App.js

    export const UserContext = createContext()
    export const ThemContext = createContext()
    export const TokenContext = createContext()
    

    user-context.js

    import { useContext } from "react";
    import { TokenContext, UserContext } from "../App";
    export function useUserContext() {
        const user = useContext(UserContext);
        const token = useContext(TokenContext);
        return [user, token];
    }
    

    useContext的Demo

    import React, { useCallback, useContext } from 'react'
    import { TokenContext, UserContext } from '../App'
    import { useUserContext } from '../hooks/user-hook';
    
    function CustomContextShare() {
        const [user, token] = useUserContext()
        console.log(user, token);
        return (
            <div>
                <h2>CustomContextShare</h2>
            </div>
        )
    }
    export default CustomContextShare
    

    3、自定义useScrollPosition

    scroll-position-hook.js

    import { useEffect, useState } from "react";
    export function useScrollPosition() {
        const [scrollPosition, setScrollPosition] = useState(0);
        useEffect(() => {
            const handleScroll = () => {
                setScrollPosition(window.scrollY)
                console.log(window.scrollY);
            }
            document.addEventListener("scroll", handleScroll)
            return (() => {
                document.removeEventListener("scroll", handleScroll)
            })
        })
        return scrollPosition;
    }
    

    CustomScrollPositionhook.js

    import React, { useEffect, useState } from 'react'
    import { useScrollPosition } from '../hooks/scroll-position-hook'
    function CustomScrollPositionhook() {
        const scrollPosition = useScrollPosition()
    
        return (
            <div style={{padding: "1000px 0"}}>
                <h2 style={{position: "fixed", left: 0, right: 0, top: 0}}>CustomScrollPositionhook {scrollPosition}</h2>
            </div>
        )
    }
    export default CustomScrollPositionhook
    

    4、练习

    local-store-hook.js

    import { useEffect, useState } from "react";
    export function useLocalStorage(key) {
        const [name, setName] = useState(() => {
            const name = JSON.parse(window.localStorage.getItem(key));
            return name;
        })
    
        useEffect(() => {
            window.localStorage.setItem(key, JSON.stringify(name));
        }, [name])
        return [name, setName];
    }
    

    CustomDataStorageHook.js

    import React, { useEffect, useState } from 'react'
    import { useLocalStorage } from '../hooks/local-store-hook';
    function CustomDataStorageHook() {
    
        const [name, setName] = useLocalStorage("name")
        return (
            <div>
                <h2>CustomDataStorageHook-{name}</h2>
                <button onClick={e => setName("codeWhy")}>设置name</button>
            </div>
        )
    }
    export default CustomDataStorageHook
    

    useState源码

    // ReactFiberHooks.new.js line 346
    export function renderWithHooks<Props, SecondArg>(
      current: Fiber | null,
      workInProgress: Fiber,
      Component: (p: Props, arg: SecondArg) => any,
      props: Props,
      secondArg: SecondArg,
      nextRenderLanes: Lanes,
    ): any {
      renderLanes = nextRenderLanes;
      currentlyRenderingFiber = workInProgress;
    
      if (__DEV__) {
        hookTypesDev =
          current !== null
            ? ((current._debugHookTypes: any): Array<HookType>)
            : null;
        hookTypesUpdateIndexDev = -1;
        // Used for hot reloading:
        ignorePreviousDependencies =
          current !== null && current.type !== workInProgress.type;
      }
    
      workInProgress.memoizedState = null;
      workInProgress.updateQueue = null;
      workInProgress.lanes = NoLanes;
    
      // The following should have already been reset
      // currentHook = null;
      // workInProgressHook = null;
    
      // didScheduleRenderPhaseUpdate = false;
    
      // TODO Warn if no hooks are used at all during mount, then some are used during update.
      // Currently we will identify the update render as a mount because memoizedState === null.
      // This is tricky because it's valid for certain types of components (e.g. React.lazy)
    
      // Using memoizedState to differentiate between mount/update only works if at least one stateful hook is used.
      // Non-stateful hooks (e.g. context) don't get added to memoizedState,
      // so memoizedState would be null during updates and mounts.
      if (__DEV__) {
        if (current !== null && current.memoizedState !== null) {
          ReactCurrentDispatcher.current = HooksDispatcherOnUpdateInDEV;
        } else if (hookTypesDev !== null) {
          // This dispatcher handles an edge case where a component is updating,
          // but no stateful hooks have been used.
          // We want to match the production code behavior (which will use HooksDispatcherOnMount),
          // but with the extra DEV validation to ensure hooks ordering hasn't changed.
          // This dispatcher does that.
          ReactCurrentDispatcher.current = HooksDispatcherOnMountWithHookTypesInDEV;
        } else {
          ReactCurrentDispatcher.current = HooksDispatcherOnMountInDEV;
        }
      } else {
        ReactCurrentDispatcher.current =
          current === null || current.memoizedState === null // 保存state状态
            ? HooksDispatcherOnMount
            : HooksDispatcherOnUpdate;
      }
    
      let children = Component(props, secondArg);
    
      // Check if there was a render phase update
      if (didScheduleRenderPhaseUpdateDuringThisPass) {
        // Keep rendering in a loop for as long as render phase updates continue to
        // be scheduled. Use a counter to prevent infinite loops.
        let numberOfReRenders: number = 0;
        do {
          didScheduleRenderPhaseUpdateDuringThisPass = false;
          invariant(
            numberOfReRenders < RE_RENDER_LIMIT,
            'Too many re-renders. React limits the number of renders to prevent ' +
              'an infinite loop.',
          );
    
          numberOfReRenders += 1;
          if (__DEV__) {
            // Even when hot reloading, allow dependencies to stabilize
            // after first render to prevent infinite render phase updates.
            ignorePreviousDependencies = false;
          }
    
          // Start over from the beginning of the list
          currentHook = null;
          workInProgressHook = null;
    
          workInProgress.updateQueue = null;
    
          if (__DEV__) {
            // Also validate hook order for cascading updates.
            hookTypesUpdateIndexDev = -1;
          }
    
          ReactCurrentDispatcher.current = __DEV__
            ? HooksDispatcherOnRerenderInDEV
            : HooksDispatcherOnRerender;
    
          children = Component(props, secondArg);
        } while (didScheduleRenderPhaseUpdateDuringThisPass);
      }
    
      // We can assume the previous dispatcher is always this one, since we set it
      // at the beginning of the render phase and there's no re-entrancy.
      ReactCurrentDispatcher.current = ContextOnlyDispatcher;
    
      if (__DEV__) {
        workInProgress._debugHookTypes = hookTypesDev;
      }
    
      // This check uses currentHook so that it works the same in DEV and prod bundles.
      // hookTypesDev could catch more cases (e.g. context) but only in DEV bundles.
      const didRenderTooFewHooks =
        currentHook !== null && currentHook.next !== null;
    
      renderLanes = NoLanes;
      currentlyRenderingFiber = (null: any);
    
      currentHook = null;
      workInProgressHook = null;
    
      if (__DEV__) {
        currentHookNameInDev = null;
        hookTypesDev = null;
        hookTypesUpdateIndexDev = -1;
      }
    
      didScheduleRenderPhaseUpdate = false;
    
      invariant(
        !didRenderTooFewHooks,
        'Rendered fewer hooks than expected. This may be caused by an accidental ' +
          'early return statement.',
      );
    
      return children;
    }
    

    相关文章

      网友评论

          本文标题:[react]24、自定义hooks

          本文链接:https://www.haomeiwen.com/subject/bmdrxrtx.html