React 類元件的一些基本概念
阿新 • • 發佈:2021-06-23
談談 React 裡面的一些型別及使用場景
- React.Component<P,S,SS>, 這個型別絕對是 react 裡面的一哥,P 是 props, S: State, SS: SnapShot. 凡是 class 元件,都得繼承這個基類。下面是這個類的 type.d.ts. 生命週期主要參考下面這副圖 類生命週期 .
這幅圖是新舊生命週期執行的圖,橘色是 deprecated, 綠色是新的,新老不能同時定義,否則會報錯。
interface Component<P = {}, S = {}, SS = any> extends ComponentLifecycle<P, S, SS> {}
ComponentLifecycle
interface ComponentLifecycle<P, S, SS = any> extends NewLifecycle<P, S, SS>, DeprecatedLifecycle<P, S> { /** * Called immediately after a component is mounted. Setting state here will trigger re-rendering. */ componentDidMount?(): void; /** * Called to determine whether the change in props and state should trigger a re-render. * * `Component` always returns true. * `PureComponent` implements a shallow comparison on props and state and returns true if any * props or states have changed. * * If false is returned, `Component#render`, `componentWillUpdate` * and `componentDidUpdate` will not be called. */ shouldComponentUpdate?( nextProps: Readonly<P>, nextState: Readonly<S>, nextContext: any ): boolean; /** * Called immediately before a component is destroyed. Perform any necessary cleanup in this method, such as * cancelled network requests, or cleaning up any DOM elements created in `componentDidMount`. */ componentWillUnmount?(): void; /** * Catches exceptions generated in descendant components. Unhandled exceptions will cause * the entire component tree to unmount. */ componentDidCatch?(error: Error, errorInfo: ErrorInfo): void; }
NewLifecycle
interface NewLifecycle<P, S, SS> { /** * Runs before React applies the result of `render` to the document, and * returns an object to be given to componentDidUpdate. Useful for saving * things such as scroll position before `render` causes changes to it. * * Note: the presence of getSnapshotBeforeUpdate prevents any of the deprecated * lifecycle events from running. */ getSnapshotBeforeUpdate?( prevProps: Readonly<P>, prevState: Readonly<S> ): SS | null; /** * Called immediately after updating occurs. Not called for the initial render. * * The snapshot is only present if getSnapshotBeforeUpdate is present and returns non-null. */ componentDidUpdate?( prevProps: Readonly<P>, prevState: Readonly<S>, snapshot?: SS ): void; }
DeprecatedLifecycle
interface DeprecatedLifecycle<P, S> {
/**
* Called immediately before mounting occurs, and before `Component#render`.
* Avoid introducing any side-effects or subscriptions in this method.
*
* Note: the presence of getSnapshotBeforeUpdate or getDerivedStateFromProps
* prevents this from being invoked.
*
* @deprecated 16.3, use componentDidMount or the constructor instead; will stop working in React 17
* @see https://reactjs.org/blog/2018/03/27/update-on-async-rendering.html#initializing-state
* @see https://reactjs.org/blog/2018/03/27/update-on-async-rendering.html#gradual-migration-path
*/
componentWillMount?(): void;
/**
* Called immediately before mounting occurs, and before `Component#render`.
* Avoid introducing any side-effects or subscriptions in this method.
*
* This method will not stop working in React 17.
*
* Note: the presence of getSnapshotBeforeUpdate or getDerivedStateFromProps
* prevents this from being invoked.
*
* @deprecated 16.3, use componentDidMount or the constructor instead
* @see https://reactjs.org/blog/2018/03/27/update-on-async-rendering.html#initializing-state
* @see https://reactjs.org/blog/2018/03/27/update-on-async-rendering.html#gradual-migration-path
*/
UNSAFE_componentWillMount?(): void;
/**
* Called when the component may be receiving new props.
* React may call this even if props have not changed, so be sure to compare new and existing
* props if you only want to handle changes.
*
* Calling `Component#setState` generally does not trigger this method.
*
* Note: the presence of getSnapshotBeforeUpdate or getDerivedStateFromProps
* prevents this from being invoked.
*
* @deprecated 16.3, use static getDerivedStateFromProps instead; will stop working in React 17
* @see https://reactjs.org/blog/2018/03/27/update-on-async-rendering.html#updating-state-based-on-props
* @see https://reactjs.org/blog/2018/03/27/update-on-async-rendering.html#gradual-migration-path
*/
componentWillReceiveProps?(nextProps: Readonly<P>, nextContext: any): void;
/**
* Called when the component may be receiving new props.
* React may call this even if props have not changed, so be sure to compare new and existing
* props if you only want to handle changes.
*
* Calling `Component#setState` generally does not trigger this method.
*
* This method will not stop working in React 17.
*
* Note: the presence of getSnapshotBeforeUpdate or getDerivedStateFromProps
* prevents this from being invoked.
*
* @deprecated 16.3, use static getDerivedStateFromProps instead
* @see https://reactjs.org/blog/2018/03/27/update-on-async-rendering.html#updating-state-based-on-props
* @see https://reactjs.org/blog/2018/03/27/update-on-async-rendering.html#gradual-migration-path
*/
UNSAFE_componentWillReceiveProps?(
nextProps: Readonly<P>,
nextContext: any
): void;
/**
* Called immediately before rendering when new props or state is received. Not called for the initial render.
*
* Note: You cannot call `Component#setState` here.
*
* Note: the presence of getSnapshotBeforeUpdate or getDerivedStateFromProps
* prevents this from being invoked.
*
* @deprecated 16.3, use getSnapshotBeforeUpdate instead; will stop working in React 17
* @see https://reactjs.org/blog/2018/03/27/update-on-async-rendering.html#reading-dom-properties-before-an-update
* @see https://reactjs.org/blog/2018/03/27/update-on-async-rendering.html#gradual-migration-path
*/
componentWillUpdate?(
nextProps: Readonly<P>,
nextState: Readonly<S>,
nextContext: any
): void;
/**
* Called immediately before rendering when new props or state is received. Not called for the initial render.
*
* Note: You cannot call `Component#setState` here.
*
* This method will not stop working in React 17.
*
* Note: the presence of getSnapshotBeforeUpdate or getDerivedStateFromProps
* prevents this from being invoked.
*
* @deprecated 16.3, use getSnapshotBeforeUpdate instead
* @see https://reactjs.org/blog/2018/03/27/update-on-async-rendering.html#reading-dom-properties-before-an-update
* @see https://reactjs.org/blog/2018/03/27/update-on-async-rendering.html#gradual-migration-path
*/
UNSAFE_componentWillUpdate?(
nextProps: Readonly<P>,
nextState: Readonly<S>,
nextContext: any
): void;
}
- React.FC
函式元件,這個是絕對的後起之秀,react 的希望,官方推薦使用定義元件的方案。 它的相關型別比較多。例如 無狀態元件,函式元件等等,它的執行過程就相對簡單多了,就是函式的執行,從上往下執行,注意幾個底層的 hook 函式
const [val,setVal]=React.useState<S>(initialState)
, 它的特點是函式第一次執行的時候,或者val !==undefine
的時候才會執行,第二次以後就不會執行了,其實是內部直接返回了. 注意,它必須至於 function 內部的最頂層,不能存在於任何塊作用域。否則會報錯。const context = React.useContext(Context)
, 跟上面的類似,也是第一次執行,以後只有 Context.provider 的值變化的時候才會執行。const ref = React.useRef<r>()
, 這個個人絕對可以看作是上面兩個的基礎,它的作用是確保 ref, 在函式內部永遠唯一,不管函式執行多少次。
一句話,hook 函式的目的只有一個,就是確保函式在 React setState 多次時,有些變數能夠被共享,相當於提供了一個上下文。
我們來看一下FC的型別定義。
/**
* @deprecated as of recent React versions, function components can no
* longer be considered 'stateless'. Please use `FunctionComponent` instead.
*
* @see [React Hooks](https://reactjs.org/docs/hooks-intro.html)
*/
type StatelessComponent<P = {}> = FunctionComponent<P>;
type FC<P = {}> = FunctionComponent<P>;
interface FunctionComponent<P = {}> {
(props: PropsWithChildren<P>, context?: any): ReactElement<any, any> | null;
propTypes?: WeakValidationMap<P>;
contextTypes?: ValidationMap<any>;
defaultProps?: Partial<P>;
displayName?: string;
}
React.ForwardRefRenderFunction<TRef,P>
以及著名的React.forwardRef<TRef,P>()
它是用來定義元件,同時將 ref 傳遞給內部的元件,因為一般情況下,ref 會被賦予當前元件的值,而這個方法的作用就是洞穿當前元件,將 ref 往下傳遞。 我們來看一下型別定義,很清楚.forwardRef() 返回的其實是一個方法,同時含有一下屬性.
interface ForwardRefRenderFunction<T, P = {}> {
(props: PropsWithChildren<P>, ref: ForwardedRef<T>): ReactElement | null;
displayName?: string;
// explicit rejected with `never` required due to
// https://github.com/microsoft/TypeScript/issues/36826
/**
* defaultProps are not supported on render functions
*/
defaultProps?: never;
/**
* propTypes are not supported on render functions
*/
propTypes?: never;
}
function forwardRef<T, P = {}>(
render: ForwardRefRenderFunction<T, P>
): ForwardRefExoticComponent<PropsWithoutRef<P> & RefAttributes<T>>;
// will show `ForwardRef(${Component.displayName || Component.name})` in devtools by default,
// but can be given its own specific name
interface ForwardRefExoticComponent<P> extends NamedExoticComponent<P> {
defaultProps?: Partial<P>;
propTypes?: WeakValidationMap<P>;
}
interface NamedExoticComponent<P = {}> extends ExoticComponent<P> {
displayName?: string;
}
// TODO: similar to how Fragment is actually a symbol, the values returned from createContext,
// forwardRef and memo are actually objects that are treated specially by the renderer; see:
// https://github.com/facebook/react/blob/v16.6.0/packages/react/src/ReactContext.js#L35-L48
// https://github.com/facebook/react/blob/v16.6.0/packages/react/src/forwardRef.js#L42-L45
// https://github.com/facebook/react/blob/v16.6.0/packages/react/src/memo.js#L27-L31
// However, we have no way of telling the JSX parser that it's a JSX element type or its props other than
// by pretending to be a normal component.
//
// We don't just use ComponentType or SFC types because you are not supposed to attach statics to this
// object, but rather to the original function.
interface ExoticComponent<P = {}> {
/**
* **NOTE**: Exotic components are not callable.
*/
(props: P): ReactElement | null;
readonly $$typeof: symbol;
}
memo() vs React.useMemo() vs React.PureComponent
React.memo() 它是一個 HOC,只有當內部元件的輸入引數變化了,或者內部元件使用了 useState,useReducer,useContext 這些 hook 函式,它才會觸發 render. 否則就直接返回上次 render 的內部元件.
簡單的說,memo()函式執行後返回的是內部元件,跟內部元件幾乎一模一樣,只是,它的更新規則變了。同時它的比較還是淺比較。它屬於 HOC。
useMemo() 它是 hook 函式,它將返回的值進行快取,除非以來項發生變化,否則值不變。它本身是一個函式。
這裡還要提到 useCallback(), 跟 useMemo() 的唯一差別是它快取的是函式。通常用於事件響應函式的快取等。
PureComponent, 它跟普通的 Component 的差別是預設實現了 componentshouldupdate() 方法,跟 memo() 類似。
function memo<T extends ComponentType<any>>(
Component: T,
propsAreEqual?: (
prevProps: Readonly<ComponentProps<T>>,
nextProps: Readonly<ComponentProps<T>>
) => boolean
): MemoExoticComponent<T>;
// will show `Memo(${Component.displayName || Component.name})` in devtools by default,
// but can be given its own specific name
type MemoExoticComponent<T extends ComponentType<any>> = NamedExoticComponent<
ComponentPropsWithRef<T>
> & {
readonly type: T;
};
// I made 'inputs' required here and in useMemo as there's no point to memoizing without the memoization key
// useCallback(X) is identical to just using X, useMemo(() => Y) is identical to just using Y.
/**
* `useCallback` will return a memoized version of the callback that only changes if one of the `inputs`
* has changed.
*
* @version 16.8.0
* @see https://reactjs.org/docs/hooks-reference.html#usecallback
*/
// TODO (TypeScript 3.0): <T extends (...args: never[]) => unknown>
function useCallback<T extends (...args: any[]) => any>(
callback: T,
deps: DependencyList
): T;
/**
* `useMemo` will only recompute the memoized value when one of the `deps` has changed.
*
* Usage note: if calling `useMemo` with a referentially stable function, also give it as the input in
* the second argument.
*
* ```ts
* function expensive () { ... }
*
* function Component () {
* const expensiveResult = useMemo(expensive, [expensive])
* return ...
* }
* ```
*
* @version 16.8.0
* @see https://reactjs.org/docs/hooks-reference.html#usememo
*/
// allow undefined, but don't make it optional as that is very likely a mistake
function useMemo<T>(factory: () => T, deps: DependencyList | undefined): T;
class PureComponent<P = {}, S = {}, SS = any> extends Component<P, S, SS> {}
- React.cloneElement()