zoukankan      html  css  js  c++  java
  • TypeScript and React: Hooks

    React Hooks

    Hooks以前将“无状态”功能组件放到了……基本上,传统类组件都可以做到。使用更干净的API!在React 16.7中发布后不久,DefinitelyTyped中的React类型也得到了更新。了解如何在TypeScript中使用钩子!

    useState

    import React, { FunctionComponent, useState } from 'react';
    
    const Counter:FunctionComponent<{ initial?: number }> = ({ initial = 0 }) => {
      const [clicks, setClicks] = useState(initial);
      return <>
        <p>Clicks: {clicks}</p>
        <button onClick={() => setClicks(clicks+1)}>+</button>
        <button onClick={() => setClicks(clicks-1)}>-</button>
      </>
    }
    

    useState 的使用方式

    const [keys, setKeys] = useState<{ currentOpenSubs: string[], currentSideMenu: string }>({
        currentSideMenu: '',
        currentOpenSubs: [],
    });
    

    useEffect

    useEffect在这里有所有副作用。添加事件侦听器,更改文档中的内容,获取数据。一切你所使用的(组件生命周期方法componentDidUpdatecomponentDidMountcomponentWillUnmount)的方法签名是非常简单的。它接受两个参数:

    • 无需任何参数即可调用的函数。这是您要调用的副作用。
    • 类型的值数组any。此参数是可选的。如果不提供,则每次组件更新时都会调用提供的功能。如果这样做,React将检查这些值是否确实发生了变化,并且仅在存在差异时才触发函数。
    useEffect(() => {
      const handler = () => {
        document.title = window.width;
      }
      window.addEventListener('resize', handler);
    
      return true;
      return () => {
        window.removeEventListener('resize', handler);
      }
    })
    

    useContext

    useContext允许您从组件中的任何位置访问上下文属性。非常类似于Context.Consumer 类组件中的do。类型推断在这里非常出色,您不需要使用任何TypeScript特定的语言功能就可以完成所有工作:

    import React, { useContext } from 'react';
    
    export const LanguageContext = React.createContext({ lang: 'en' });
    
    const Display = () => {
      const { lang } = useContext(LanguageContext);
      return <>
        <p>Your selected language: {lang}</p>
      </>
    }
    

    useRef

    function TextInputWithFocusButton() {
      
      const inputEl = useRef<HTMLInputElement>(null);
      const onButtonClick = () => {
       
        if(inputEl && inputEl.current) {
          inputEl.current.focus();
        } 
      };
      return (
        <>
          <input ref={inputEl} type="text" />
          <button onClick={onButtonClick}>Focus the input</button>
        </>
      );
    }
    

    useMemo-useCallback

    useMemo返回一个 memoized 值。 传递“创建”函数和依赖项数组。useMemo 只会在其中一个依赖项发生更改时重新计算 memoized 值。此优化有助于避免在每个渲染上进行昂贵的计算。

    const memoizedValue = useMemo(() => computeExpensiveValue( a, b),[ a, b ]);
    

    useMemo 在渲染过程中传递的函数会运行。不要做那些在渲染时通常不会做的事情。例如,副作用属于 useEffect,而不是 useMemo。

    您知道useEffect,可以通过向其传递一些参数来影响某些函数的执行。React检查这些参数是否已更改,并且仅在存在差异的情况下才会执行此功能。
    useMemo做类似的事情。假设您有大量计算方法,并且只想在其参数更改时运行它们,而不是每次组件更新时都运行它们。useMemo返回记忆的结果,并且仅在参数更改时才执行回调函数。

    function getHistogram(image: ImageData): number[] {
      ...
      return histogram;
    }
    
    function Histogram() {
      ...
      const histogram = useMemo(() => getHistogram(imageData), [imageData]);
    }
    

    useCallback非常相似。实际上,这也是可以表达的捷径useMemo。但是它返回一个回调函数,而不是一个值。

    const memoCallback = useCallback((a: number) => {
      // doSomething
    }, [a])
    

    useMemo和useCallback的作用有点像啊,那它们之间有什么区别呢?

    • useCallback 和 useMemo 都可缓存函数的引用或值。
    • 从更细的使用角度来说 useCallback 缓存函数的引用,useMemo 缓存计算数据的值。

    useReducer

    如果您以前使用过Redux,则应该很熟悉。useReducer接受 3 个参数(reducer,initialState,init)并返回当前的 state 以及与其配套的 dispatch 方法。reducer 是如下形式的函数(state, action) => newState;initialState 是一个 JavaScript 对象;而 init 参数是一个惰性初始化函数,可以让你延迟加载初始状态。
    const [state,dispatch] = useReducer(reducer,initialState,init);

    type ActionType = {
      type: 'reset' | 'decrement' | 'increment'
    }
    
    const initialState = { count: 0 };
    
    // We only need to set the type here ...
    function reducer(state, action: ActionType) {
      switch (action.type) {
        // ... to make sure that we don't have any other strings here ...
        case 'reset':
          return initialState;
        case 'increment':
          return { count: state.count + 1 };
        case 'decrement':
          return { count: state.count - 1 };
        default:
          return state;
      }
    }
    
    function Counter({ initialCount = 0 }) {
      const [state, dispatch] = useReducer(reducer, { count: initialCount });
      return (
        <>
          Count: {state.count}
          { /* and can dispatch certain events here */ }
          <button onClick={() => dispatch({ type: 'reset' })}>
            Reset
          </button>
          <button onClick={() => dispatch({ type: 'increment' })}>+</button>
          <button onClick={() => dispatch({ type: 'decrement' })}>-</button>
        </>
      );
    }
    

    React-redux hooks

    React Redux 现在提供了一系列 hook APIs 作为现在 connect() 高阶组件的替代品。这些 APIs 允许你,在不使用 connect() 包裹组件的情况下,订阅 Redux 的 store,和 分发(dispatch) actions。

    useSelector()

    const result : any = useSelector(selector : Function, equalityFn? : Function)
    

    基本用法

    import React from 'react'
    import { useSelector } from 'react-redux'
    
    export const CounterComponent = () => {
      const counter = useSelector(state => state.counter)
      return <div>{counter}</div>
    }
    

    通过闭包使用 props 来选择取回什么状态:

    import React from 'react'
    import { useSelector } from 'react-redux'
    
    export const TodoListItem = props => {
      const todo = useSelector(state => state.todos[props.id])
      return <div>{todo.text}</div>
    }
    

    **使用记忆化的 selectors 函数**
    当像上方展示的那样,在使用 useSelector 时使用单行箭头函数,会导致在每次渲染期间都会创建一个新的 selector 函数。可以看出,这样的 selector 函数并没有维持任何的内部状态。但是,记忆化的 selectors 函数 (通过 reselect 库中 的 createSelector 创建) 含有内部状态,所以在使用它们时必须小心。

    当一个 selector 函数依赖于某个 状态(state) 时,确保函数声明在组件之外,这样就不会导致相同的 selector 函数在每一次渲染时都被重复创建:

    import React from 'react'
    import { useSelector } from 'react-redux'
    import { createSelector } from 'reselect'
    
    const selectNumOfDoneTodos = createSelector(
      state => state.todos,
      todos => todos.filter(todo => todo.isDone).length
    )
    
    export const DoneTodosCounter = () => {
      const NumOfDoneTodos = useSelector(selectNumOfDoneTodos)
      return <div>{NumOfDoneTodos}</div>
    }
    
    export const App = () => {
      return (
        <>
          <span>Number of done todos:</span>
          <DoneTodosCounter />
        </>
      )
    }
    

    被移除的:useActions()

    import { useMemo, DependencyList } from 'react';
    import { bindActionCreators } from 'redux';
    import { useDispatch } from 'react-redux';
    // 这个主要是用于配合saga  处理异步请求
    import {
      bindPromiseCreators,
      PromiseCreator,
      ActionCreatorFunction,
      Routine,
    } from 'redux-saga-routines';
    
    type actionType = Routine | PromiseCreator | ActionCreatorFunction;
    
    // hooks 一定要以use开头
    function useActions(
      actions: {
        [kye: string]: actionType;
      },
      deps?: DependencyList | undefined
    ): any {
    
      const dispatch = useDispatch();
    
      return useMemo(() => {
        const newActions = actions;
        const keys = Object.keys(actions);
        keys.forEach((key: string) => {
          if( newActions[key].length === 2 ) {
            newActions[key] = bindPromiseCreators((actions[key] as PromiseCreator), dispatch);
          } else {
            newActions[key] = bindActionCreators((actions[key] as Routine), dispatch);
          }
        })
    
        return newActions;
      }, deps ? [dispatch, ...deps] : [dispatch]);
    
    }
    

    useAction的使用方式

     const { topMenu, currentSidebar, theme, drawer, primaryColor }  = useSelector((state: IState) => state.menu);
    
     const [collapsed, setCollapsed] = useState(false);
    
     const actions = useActions({
        setMenu: menuAction.setMenu,
        setDrawer: menuAction.setDrawer,
        setTheme: menuAction.setTheme,
     });
    
    const handleSettingClick = useCallback((values) => {
        actions.setTheme(values);
      }, [actions]);
    

    useDispatch() 这个 hook 返回 Redux store 的 分发(dispatch) 函数的引用。你也许会使用来 分发(dispatch) 某些需要的 action。

    import React from 'react'
    import { useDispatch } from 'react-redux'
    
    export const CounterComponent = ({ value }) => {
      const dispatch = useDispatch()
    
      return (
        <div>
          <span>{value}</span>
          <button onClick={() => dispatch({ type: 'increment-counter' })}>
            Increment counter
          </button>
        </div>
      )
    }
    

    在将一个使用了 dispatch 函数的回调函数传递给子组件时,建议使用 useCallback 函数将回调函数记忆化,防止因为回调函数引用的变化导致不必要的渲染。

    这里的建议其实和 dispatch 没关系,无论是否使用 dispatch,你都应该确保回调函数不会无故变化,然后导致不必要的重渲染。之所以和 dispatch 没关系,是因为,一旦 dispatch 变化,useCallback 会重新创建回调函数,回调函数的引用铁定发生了变化,然而导致不必要的重渲染。

    import React, { useCallback } from 'react'
    import { useDispatch } from 'react-redux'
    
    export const CounterComponent = ({ value }) => {
      const dispatch = useDispatch()
      const incrementCounter = useCallback(
        () => dispatch({ type: 'increment-counter' }),
        [dispatch]
      )
    
      return (
        <div>
          <span>{value}</span>
          <MyIncrementButton onIncrement={incrementCounter} />
        </div>
      )
    }
    
    export const MyIncrementButton = React.memo(({ onIncrement }) => (
      <button onClick={onIncrement}>Increment counter</button>
    ))
    

    useStore() 这个 hook 返回传递给 组件的 Redux sotore 的引用。

    这个 hook 也许不应该被经常使用。 你应该将 useSelector() 作为你的首选。但是,在一些不常见的场景下,你需要访问 store,这个还是有用的,比如替换 store 的 reducers。

    import React from 'react'
    import { useStore } from 'react-redux'
    
    export const CounterComponent = ({ value }) => {
      const store = useStore()
      return <div>{store.getState()}</div>
    }
    
  • 相关阅读:
    FPGA学习之基本结构
    凸优化和机器学习
    第6篇如何访问pod
    吉日嘎拉DotNet.BusinessV4.2中的一处bug,及我的修复和扩展
    吉日嘎拉C#快速开发平台V4.0到V4.2升级记
    布隆过滤器简介及实现-----源自数学之美
    poj [1753]
    Zookeeper Hello World
    获取用户真实IP,php实现
    mysql中engine=innodb和engine=myisam的区别
  • 原文地址:https://www.cnblogs.com/boyGdm/p/14101557.html
Copyright © 2011-2022 走看看