最新国产好看的视频,伊人天堂AV在线,国产Aaaaaa视频,蜜臀视频在线观看一区,人妻av色图,密臀久久久精品影片,青青视频免费观看毛片,久草在线观看视,国产三级精品色情在线

react?Table準(zhǔn)備Spin?Empty?ConfigProvider組件實(shí)現(xiàn)

 更新時(shí)間:2023年02月01日 09:54:59   作者:孟祥_成都  
這篇文章主要為大家介紹了react?Table準(zhǔn)備Spin、Empty、ConfigProvider組件實(shí)現(xiàn)詳解,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪<BR>

前言

繼續(xù)搞react組件庫,該寫table了,學(xué)習(xí)了arco design的table的運(yùn)行流程,發(fā)現(xiàn)準(zhǔn)備工作還是挺多的,我們就先解決以下問題吧!

比如你要配置國(guó)際化,組件庫的所有組件都要共享當(dāng)前語言的變量,比如是中文,還是英文,這樣組件才能渲染對(duì)應(yīng)國(guó)家的字符串。

也就是說,你自己的組件庫有什么想全局共享的變量,就寫在這個(gè)組件里。

table使用的地方

  const {
    getPrefixCls, // 獲取css前綴
    loadingElement, // loading顯示的組件
    size: ctxSize, // size默認(rèn)值
    renderEmpty, // 空數(shù)據(jù)時(shí)Empty組件顯示的內(nèi)容
    componentConfig, // 全局component的config
  } = useContext(ConfigContext);

我簡(jiǎn)單解釋一下,getPrefixCls獲取了組件的css前綴,比如arco deisgn 的前綴自然是arco了,他們的組件的所有css都會(huì)加上這個(gè)前綴,現(xiàn)在組件庫都這么玩。

其他的就不詳細(xì)描述了,比如table請(qǐng)求數(shù)據(jù)有l(wèi)oading,你想自定義loading樣式可以在loadingElement屬性上配置等等,也就是說全局你自定義的loading組件,所有組件都會(huì)共享,不用你一個(gè)一個(gè)去配置了。

而這里的 useContext(ConfigContext) ConfigContext就是ConfigProvider組件創(chuàng)建的context,類似這樣(細(xì)節(jié)不用糾結(jié),后面我們會(huì)實(shí)現(xiàn)這個(gè)組件):

export const ConfigContext = createContext<ConfigProviderProps>({
  getPrefixCls: (componentName: string, customPrefix?: string) => `${customPrefix || defaultProps.prefixCls}-${componentName}`,
  ...defaultProps,
});
 <ConfigContext.Provider value={config}>{children}</ConfigContext.Provider>;

Spin組件就是顯示loading態(tài)的組件,這里改造了arco的Spin組件,主要添加了樣式層,我認(rèn)可將樣式層和js控制的html,也就是jsx分層

主要體現(xiàn)在,組件里新增getClassnames和getStyles兩個(gè)函數(shù),配合css,收斂所有組件的樣式。

在復(fù)雜組件里,我還會(huì)嘗試收斂數(shù)據(jù)層和渲染層,但是spin組件和后面的empty組件太簡(jiǎn)單了,就沒有做這步

在table中這樣使用

<Spin element={loadingElement} {...loading}>
        {renderTable()}
</Spin>

Empty組件

table組件沒有數(shù)據(jù)的時(shí)候就會(huì)顯示它

這篇基本全是代碼,大家簡(jiǎn)單看看就好,重點(diǎn)是下一篇將table組件,這里主要是做個(gè)記錄

目錄結(jié)構(gòu)

├── ConfigProvider 
│   ├── config // 配置文件
│   │   ├── constants.tsx // 常量
│   │   └── utils_fns // 工具函數(shù)文件夾
│   ├── index.tsx
│   └── interface.ts // ts定義文件
├── Empty
│   ├── config  // 配置文件
│   │   ├── constants.ts
│   │   └── utils_fns // 工具函數(shù)文件夾
│   │       ├── getDesDefault.ts
│   │       ├── xxx
│   │       └── index.ts
│   ├── index.tsx
│   ├── interface.ts  // ts定義文件
│   └── style // 樣式文件
│       ├── index.less
│       └── index.ts
├── Icon // Icon是單獨(dú)一個(gè)項(xiàng)目,自動(dòng)化生成Icon,還有點(diǎn)復(fù)雜度的,這個(gè)后面組件庫詳細(xì)講吧
│   ├── index.tsx
│   └── style
│       └── index.less
├── Spin
│   ├── config
│   │   ├── hooks // 自定義hook
│   │   └── utils_fns
│   ├── index.tsx
│   ├── interface.ts
│   └── style
│       ├── index.less
│       └── index.ts
├── Table
│   ├── config
│   │   └── util_fns
│   └── table.tsx
├── config // 公共配置文件
│   ├── index.ts
│   └── util_fns
│       ├── index.ts
│       └── pickDataAttributes.ts
├── index.ts
├── locale // 國(guó)際化文件夾
│   ├── default.tsx
│   ├── en-US.tsx
│   ├── interface.tsx
│   └── zh-CN.tsx
└── style // 樣式文件夾
    ├── base.less
    ├── common.less
    ├── index.less
    ├── normalize.less
    └── theme

開搞ConfigProvider

index.tsx,詳情見注釋

import React, { createContext, useCallback, useMemo } from 'react';
// omit相當(dāng)于lodash里的omit,不過自己寫的性能更好,因?yàn)闆]有那么多兼容性,很簡(jiǎn)單
// useMergeProps是合并外界傳入的props,和默認(rèn)props還有組件全局props的hook
import { omit, useMergeProps } from '@mx-design/utils';
// 國(guó)際化文件,默認(rèn)是中文
import defaultLocale from '../locale/default';
// 接口
import type { ConfigProviderProps } from './interface';
// componentConfig是空對(duì)象
// PREFIX_CLS是你想自定義的css樣式前綴
import { componentConfig, PREFIX_CLS } from './config/constants';
// 渲染空數(shù)據(jù)的組件
import { renderEmpty } from './config/utils_fns';
// 默認(rèn)參數(shù)
const defaultProps: ConfigProviderProps = {
  locale: defaultLocale,
  prefixCls: PREFIX_CLS,
  getPopupContainer: () => document.body,
  size: 'default',
  renderEmpty,
};
// 默認(rèn)參數(shù)
export const ConfigContext = createContext<ConfigProviderProps>({
  ...defaultProps,
});
function ConfigProvider(baseProps: ConfigProviderProps) {
  // 合并props,baseProps也就是用戶傳入的props優(yōu)先級(jí)最高
  const props = useMergeProps<ConfigProviderProps>(baseProps, defaultProps, componentConfig);
  const { prefixCls, children } = props;
// 獲取css前綴名的函數(shù)
  const getPrefixCls = useCallback(
    (componentName: string, customPrefix?: string) => {
      return `${customPrefix || prefixCls || defaultProps.prefixCls}-${componentName}`;
    },
    [prefixCls]
  );
 // 傳遞給所有子組件的數(shù)據(jù)
  const config: ConfigProviderProps = useMemo(
    () => ({
      ...omit(props, ['children']),
      getPrefixCls,
    }),
    [getPrefixCls, props]
  );
// 使用context實(shí)現(xiàn)全局變量傳遞給子組件的目的
  return <ConfigContext.Provider value={config}>{children}</ConfigContext.Provider>;
}
ConfigProvider.displayName = 'ConfigProvider';
export default ConfigProvider;
export type { ConfigProviderProps };

注意在default中,有個(gè)renderEmpty函數(shù),實(shí)現(xiàn)如下:

export function renderEmpty() {
  return <Empty />;
}

所以,我們接著看Empty組件如何實(shí)現(xiàn)

這里順便貼一下ConfigProvider中的類型定義,因?yàn)槌跗诮M件比較少,參數(shù)不多,大多數(shù)從arco deisgn源碼copy的

import { ReactNode } from 'react';
import { Locale } from '../locale/interface';
import type { EmptyProps } from '../Empty/interface';
import type { SpinProps } from '../Spin/interface';
export type ComponentConfig = {
  Empty: EmptyProps;
  Spin: SpinProps;
};
/**
 * @title ConfigProvider
 */
export interface ConfigProviderProps {
  /**
   * @zh 用于全局配置所有組件的默認(rèn)參數(shù)
   * @en Default parameters for global configuration of all components
   * @version 2.23.0
   */
  componentConfig?: ComponentConfig;
  /**
   * @zh 設(shè)置語言包
   * @en Language package setting
   */
  locale?: Locale;
  /**
   * @zh 配置組件的默認(rèn)尺寸,只會(huì)對(duì)支持`size`屬性的組件生效。
   * @en Configure the default size of the component, which will only take effect for components that support the `size` property.
   * @defaultValue default
   */
  size?: 'mini' | 'small' | 'default' | 'large';
  /**
   * @zh 全局組件類名前綴
   * @en Global ClassName prefix
   * @defaultValue arco
   */
  prefixCls?: string;
  getPrefixCls?: (componentName: string, customPrefix?: string) => string;
  /**
   * @zh 全局彈出框掛載的父級(jí)節(jié)點(diǎn)。
   * @en The parent node of the global popup.
   * @defaultValue () => document.body
   */
  getPopupContainer?: (node: HTMLElement) => Element;
  /**
   * @zh 全局的加載中圖標(biāo),作用于所有組件。
   * @en Global loading icon.
   */
  loadingElement?: ReactNode;
  /**
   * @zh 全局配置組件內(nèi)的空組件。
   * @en Empty component in component.
   * @version 2.10.0
   */
  renderEmpty?: (componentName?: string) => ReactNode;
  zIndex?: number;
  children?: ReactNode;
}

Empty組件實(shí)現(xiàn)

index.tsx

import React, { memo, useContext, forwardRef } from 'react';
import { useMergeProps } from '@mx-design/utils';
import { ConfigContext } from '../ConfigProvider';
import type { EmptyProps } from './interface';
import { emptyImage, getDesDefault } from './config/utils_fns';
import { useClassNames } from './config/hooks';
function Empty(baseProps: EmptyProps, ref) {
  // 獲取全局參數(shù)
  const { getPrefixCls, locale: globalLocale, componentConfig } = useContext(ConfigContext);
  // 合并props
  const props = useMergeProps<EmptyProps>({}, componentConfig?.Empty, baseProps);
  const { style, className, description, icon, imgSrc } = props;
  // 獲取國(guó)際化的 noData字符串
  const { noData } = globalLocale.Empty;
  // class樣式層
  const { containerCls, wrapperCls, imageCls, descriptionCls } = useClassNames({ getPrefixCls, className });
  // 獲取描述信息
  const alt = getDesDefault(description);
  return (
    <div ref={ref} className={containerCls} style={style}>
      <div className={wrapperCls}>
        <div className={imageCls}>{emptyImage({ imgSrc, alt, icon })}</div>
        <div className={descriptionCls}>{description || noData}</div>
      </div>
    </div>
  );
}
const EmptyComponent = forwardRef(Empty);
EmptyComponent.displayName = 'Empty';
export default memo(EmptyComponent);
export type { EmptyProps };

useClassNames,主要是通過useMemo緩存所有的className,一般情況下,這些className都不會(huì)變

import { cs } from '@mx-design/utils';
import { useMemo } from 'react';
import { ConfigProviderProps } from '../../../ConfigProvider';
import { EmptyProps } from '../..';
interface getClassNamesProps {
  getPrefixCls: ConfigProviderProps['getPrefixCls'];
  className: EmptyProps['className'];
}
export function useClassNames(props: getClassNamesProps) {
  const { getPrefixCls, className } = props;
  const prefixCls = getPrefixCls('empty');
  const classNames = cs(prefixCls, className);
  return useMemo(
    () => ({
      containerCls: classNames,
      wrapperCls: `${prefixCls}-wrapper`,
      imageCls: `${prefixCls}-image`,
      descriptionCls: `${prefixCls}-description`,
    }),
    [classNames, prefixCls]
  );
}

getDesDefault,

import { DEFAULT_DES } from '../constants';
export function getDesDefault(description) {
  return typeof description === 'string' ? description : DEFAULT_DES;
}

getEmptyImage

import { IconEmpty } from '@mx-design/icon';
import React from 'react';
import { IEmptyImage } from '../../interface';
export const emptyImage: IEmptyImage = ({ imgSrc, alt, icon }) => {
  return imgSrc ? <img alt={alt} src={imgSrc} /> : icon || <IconEmpty />;
};

Spin組件

也很簡(jiǎn)單,值得一提的是,你知道寫一個(gè)debounce函數(shù)怎么寫嗎,很多網(wǎng)上的人寫的簡(jiǎn)陋不堪,起碼還是有個(gè)cancel方法,好吧,要不你useEffect想在組件卸載的時(shí)候,清理debounce的定時(shí)器都沒辦法。

debounce實(shí)現(xiàn)

interface IDebounced<T extends (...args: any) => any> {
  cancel: () => void;
  (...args: any[]): ReturnType<T>;
}
export function debounce<T extends (...args: any) => any>(func: T, wait: number, immediate?: boolean): IDebounced<T> {
  let timeout: number | null;
  let result: any;
  const debounced: IDebounced<T> = function (...args) {
    const context = this;
    if (timeout) clearTimeout(timeout);
    if (immediate) {
      let callNow = !timeout;
      timeout = window.setTimeout(function () {
        timeout = null;
      }, wait);
      if (callNow) result = func.apply(context, args);
    } else {
      timeout = window.setTimeout(function () {
        result = func.apply(context, args);
      }, wait);
    }
    // Only the first time you can get the result, that is, immediate is true
    // if not,result has little meaning
    return result;
  };
  debounced.cancel = function () {
    clearTimeout(timeout!);
    timeout = null;
  };
  return debounced;
}

順便我們?cè)趯懸粋€(gè)useDebounce的hook吧,項(xiàng)目中也要用

import { debounce } from '@mx-design/utils';
import { useCallback, useEffect, useState } from 'react';
import type { SpinProps } from '../../interface';
interface debounceLoadingProps {
  delay: SpinProps['delay'];
  propLoading: SpinProps['loading'];
}
export const useDebounceLoading = function (props: debounceLoadingProps): [boolean] {
  const { delay, propLoading } = props;
  const [loading, setLoading] = useState<boolean>(delay ? false : propLoading);
  const debouncedSetLoading = useCallback(debounce(setLoading, delay), [delay]);
  const getLoading = delay ? loading : propLoading;
  useEffect(() => {
    delay && debouncedSetLoading(propLoading);
    return () => {
      debouncedSetLoading?.cancel();
    };
  }, [debouncedSetLoading, delay, propLoading]);
  return [getLoading];
};

index.tsx

import React, { useContext } from 'react';
import { useMergeProps } from '@mx-design/utils';
import { ConfigContext } from '../ConfigProvider';
import type { SpinProps } from './interface';
import InnerLoading from './InnerLoading';
import { useClassNames, useDebounceLoading } from './config/hooks';
function Spin(baseProps: SpinProps, ref) {
  const { getPrefixCls, componentConfig } = useContext(ConfigContext);
  const props = useMergeProps<SpinProps>(baseProps, {}, componentConfig?.Spin);
  const { style, className, children, loading: propLoading, size, icon, element, tip, delay, block = true } = props;
  const [loading] = useDebounceLoading({ delay, propLoading });
  const { prefixCls, wrapperCls, childrenWrapperCls, loadingLayerCls, loadingLayerInnerCls, tipCls } = useClassNames({
    getPrefixCls,
    block,
    loading,
    tip,
    children,
    className,
  });
  return (
    <div ref={ref} className={wrapperCls} style={style}>
      {children ? (
        <>
          <div className={childrenWrapperCls}>{children}</div>
          {loading && (
            <div className={loadingLayerCls} style={{ fontSize: size }}>
              <span className={loadingLayerInnerCls}>
                <InnerLoading prefixCls={prefixCls} icon={icon} size={size} element={element} tipCls={tipCls} tip={tip} />
              </span>
            </div>
          )}
        </>
      ) : (
        <InnerLoading prefixCls={prefixCls} icon={icon} size={size} element={element} tipCls={tipCls} tip={tip} />
      )}
    </div>
  );
}
const SpinComponent = React.forwardRef<unknown, SpinProps>(Spin);
SpinComponent.displayName = 'Spin';
export default SpinComponent;
export { SpinProps };

LoadingIcon.tsx

import { IconLoading } from '@mx-design/icon';
import { cs } from '@mx-design/utils';
import React, { FC, ReactElement } from 'react';
import { ConfigProviderProps } from '../../../ConfigProvider';
import type { SpinProps } from '../../interface';
interface loadingIconProps {
  prefixCls: ConfigProviderProps['prefixCls'];
  icon: SpinProps['icon'];
  size: SpinProps['size'];
  element: SpinProps['element'];
}
export const LoadingIcon: FC<loadingIconProps> = function (props) {
  const { prefixCls, icon, size, element } = props;
  return (
    <span className={`${prefixCls}-icon`}>
      {icon
        ? // 這里可以讓傳入的icon自動(dòng)旋轉(zhuǎn)
          React.cloneElement(icon as ReactElement, {
            className: `${prefixCls}-icon-loading`,
            style: {
              fontSize: size,
            },
          })
        : element || <IconLoading className={`${prefixCls}-icon-loading`} style={{ fontSize: size }} />}
    </span>
  );
};

以上就是react Table準(zhǔn)備Spin Empty ConfigProvider組件實(shí)現(xiàn)的詳細(xì)內(nèi)容,更多關(guān)于react Table組件的資料請(qǐng)關(guān)注腳本之家其它相關(guān)文章!

相關(guān)文章

  • 使用react-activation實(shí)現(xiàn)keepAlive支持返回傳參

    使用react-activation實(shí)現(xiàn)keepAlive支持返回傳參

    本文主要介紹了使用react-activation實(shí)現(xiàn)keepAlive支持返回傳參,文中通過示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧
    2022-05-05
  • React報(bào)錯(cuò)之組件不能作為JSX組件使用的解決方法

    React報(bào)錯(cuò)之組件不能作為JSX組件使用的解決方法

    本文主要介紹了React報(bào)錯(cuò)之組件不能作為JSX組件使用的解決方法,文中通過示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧
    2022-07-07
  • react實(shí)現(xiàn)pure render時(shí)bind(this)隱患需注意!

    react實(shí)現(xiàn)pure render時(shí)bind(this)隱患需注意!

    這篇文章主要為大家詳細(xì)介紹了值得你在react實(shí)現(xiàn)pure render的時(shí)候,需要注意的bind(this)隱患,具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下
    2017-03-03
  • react native圖片解析流程詳解

    react native圖片解析流程詳解

    這篇文章主要為大家介紹了react native圖片解析流程詳解,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪
    2023-01-01
  • 在react中使用highlight.js將頁面上的代碼高亮的方法

    在react中使用highlight.js將頁面上的代碼高亮的方法

    本文通過 highlight.js 庫實(shí)現(xiàn)對(duì)文章正文 HTML 中的代碼元素自動(dòng)添加語法高亮,具有一定的參考價(jià)值,感興趣的可以了解一下
    2022-01-01
  • 說說react中引入css的方式有哪些并區(qū)別在哪

    說說react中引入css的方式有哪些并區(qū)別在哪

    本文主要介紹了說說react中引入css的方式有哪些并區(qū)別在哪,文中通過示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧
    2022-04-04
  • React實(shí)現(xiàn)骨架屏的示例代碼

    React實(shí)現(xiàn)骨架屏的示例代碼

    這篇文章主要為大家詳細(xì)介紹了如何通過React自動(dòng)化實(shí)現(xiàn)骨架屏,文中的示例代碼講解詳細(xì),感興趣的小伙伴可以跟隨小編一起學(xué)習(xí)一下
    2024-11-11
  • React使用Electron開發(fā)桌面端的詳細(xì)流程步驟

    React使用Electron開發(fā)桌面端的詳細(xì)流程步驟

    React是一個(gè)流行的JavaScript庫,用于構(gòu)建Web應(yīng)用程序,結(jié)合Electron框架,可以輕松地將React應(yīng)用程序打包為桌面應(yīng)用程序,本文詳細(xì)介紹了使用React和Electron開發(fā)桌面應(yīng)用程序的步驟,需要的朋友可以參考下
    2023-06-06
  • 漸進(jìn)式源碼解析React更新流程驅(qū)動(dòng)

    漸進(jìn)式源碼解析React更新流程驅(qū)動(dòng)

    這篇文章主要為大家介紹了漸進(jìn)式源碼解析React更新流程驅(qū)動(dòng)詳解,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪
    2023-04-04
  • React合成事件及Test Utilities在Facebook內(nèi)部進(jìn)行測(cè)試

    React合成事件及Test Utilities在Facebook內(nèi)部進(jìn)行測(cè)試

    這篇文章主要介紹了React合成事件及Test Utilities在Facebook內(nèi)部進(jìn)行測(cè)試,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪
    2022-12-12

最新評(píng)論

盐亭县| 泗洪县| 叶城县| 岗巴县| 绩溪县| 蕉岭县| 南汇区| 大竹县| 平南县| 南宁市| 奉化市| 城口县| 油尖旺区| 朝阳区| 平顶山市| 前郭尔| 贺兰县| 旅游| 应用必备| 泽州县| 手机| 安新县| 武冈市| 临湘市| 黄梅县| 汨罗市| 忻州市| 元朗区| 永修县| 抚宁县| 海南省| 江陵县| 抚州市| 峨边| 八宿县| 永修县| 隆昌县| 余姚市| 新郑市| 绍兴市| 邛崃市|