手写React组件

153 阅读9分钟

分页

思路

左边隐藏:currentPage> middleValue+1
右面隐藏:currentPage< total -middleValue
一共能显示7个按钮。 middleValue = 7/3

case1:左边不隐藏,右面隐藏
截屏2024-01-27下午9.06.58.png
case2:左右隐藏

截屏2024-01-27下午9.18.27.png case3:左面隐藏,右面不隐藏

截屏2024-01-28下午2.44.39.png

日历组件

实现日历面板

思路:直接将自己向前移动多少天后,开始循环42天

const visibleDate=(year,month)=>{
            let firstDay = new Date(year,month,1);
            let weekDay = firstDay.getDay();// 周日 0 
            weekDay = weekDay===0?7:weekDay
            let start =firstDay-weekDay *60*60*24*1000
            let arr = []
            for(let i =0;i<42;i++ ){
                arr.push(start+i*60*60*24*1000)
            }
            return arr
    }

html渲染

 {
                    row.map((rowItem,rowIndex)=>{
                        return <div key={`row${rowIndex}`}>
                       { col.map((colItem,colIndex)=>{
                        return <span className={`cell }`
                        } 
                        key={`col${colIndex}`}>
                       {new Date(arr[(rowIndex*7)+(colIndex)]).getDate()}
                    
                        </span>
                       })}
                        </div>
                    })
                }

实现面板当前月黑色字体,非当前月灰色字体,当天日期蓝色字体

 const isCurrentMonth=(date)=>{
        let {year,month} = time
        let [y,m] = getYearMonthDay(date)
        return year === y && m === month
    }
    const isToday=(day)=>{
        let [y,m,d] = getYearMonthDay(day)
        let [year,month,date] = getYearMonthDay(new Date)
        return year === y && month === month && date === d
    }
    const getYearMonthDay=(date)=>{
        let year = date.getFullYear();
        let month = date.getMonth()
        let day = date.getDate()
        return [year,month,day]
    }

html

 {
                    row.map((rowItem,rowIndex)=>{
                        return <div key={`row${rowIndex}`}>
                       { col.map((colItem,colIndex)=>{
                        return <span className={`cell 
                        ${isToday(new Date(arr[(rowIndex*7)+(colIndex)]))?'blue':''}
                        ${isCurrentMonth(new Date(arr[(rowIndex*7)+(colIndex)]))?'':'gray'}`
                        } 
                        key={`col${colIndex}`}>
                       {new Date(arr[(rowIndex*7)+(colIndex)]).getDate()}
                    
                        </span>
                       })}
                        </div>
                    })
                }

Table

Form 组件

类方法实现图

image.png

函数组件实现图

截屏2024-01-01下午5.45.15.png

截屏2024-01-01下午5.46.00.png

调用

 <Form
    initialValues={{username:'',password:''}}
    onFinish={values => {
      console.log('完成:', values);
    }}
    onFinishFailed={(errorInfo)=>{
            console.log('失败:',errorInfo);
            }}
  >
 <Field name="username" rules={[{ required: true },{ min: 3 },{validator:uniqueValidator}]}>
      <input placeholder="用户名" />
    </Field>
    <Field name="password" rules={[{require:true}]}>
      <input placeholder="密码" />
    </Field>
    <button>提交</button>
  </Form>,

数据托管

hook useState实现托管


import useStore, { FormState }from './useStore';


export const FormContext = createContext({})
export const Form = ((props, ref) => {
 
  const { form, fields, dispatch, ...restProps } = useStore()


  const passedContext= {
    dispatch,
    fields,
    initialValues,
    validateField
  }
 
  return (
    <form name={name}>
      <FormContext.Provider value={passedContext}>
        {childrenNode}
      </FormContext.Provider>
    </form>
  )
})


export default Form

import { useState, useReducer } from 'react'


function fieldsReducer(state, action) {
  switch (action.type) {
    case 'addField':
      return {
        ...state,
        [action.name]: { ...action.value }
      }
    case 'updateValue':
      return {
        ...state,
        [action.name]: { ...state[action.name], value: action.value }
      }
    case 'updateValidateResult':
      const { isValid, errors } = action.value
      return {
        ...state,
        [action.name]: { ...state[action.name], isValid, errors }
      }
    default:
      return state;
  }
}


function useStore(initialValues?) {
  // form state

  const [ fields, dispatch ] = useReducer(fieldsReducer, {})
  const getFieldValue = (key) => {
    return fields[key] && fields[key].value
  }
  const getFieldsValue = () => {
    return mapValues(fields, item => item.value)
  }
  const setFieldValue = (name: string, value: any) => {
    if (fields[name]) {
      dispatch({ type: 'updateValue', name, value })
    }
  }
  return {
    fields,
    dispatch,
    form,
    getFieldValue,
    setFieldValue
 
  }
}

export default useStore

class 实现数据托管

import React from "react";
import useForm from "./useForm";
import FieldContext from "./FieldContext";

const Form = ({initialValues,onFinish,onFinishFailed,children}) => {
  const [formInstance] = useForm();

  return (
    <form
      onSubmit={event => {
 
      }}>
      <FieldContext.Provider value={formInstance}>
        {children}
      </FieldContext.Provider>
    </form>
  );
}

export default Form;
import React from 'react';

class FormStore{
    constructor(forceRootUpdate ){
        this.forceRootUpdate= forceRootUpdate
        this.store = {}
        this.callbacks = {}
        this.fieldEntities = []

    }
    setFieldsValue = (newStore)=>{
        this.store={...this.store,...newStore}
    
    }
    getFieldValue =(name)=>{
        return this.store[name]
    }

   
    registerField = (entity) => {
        this.fieldEntities.push(entity);
    };



    getForm = () => ({
        getFieldValue: this.getFieldValue,
        getFieldsValue: this.getFieldsValue,
        setFieldValue: this.setFieldValue,
        setFieldsValue:this.setFieldsValue,
     
    }); 
}


export default function useForm() {

    let formRef = React.useRef()

    let formStore = new FormStore()
    let formInstance = formStore.getForm()
    formRef.current = formInstance
} 
    return [formRef.current]
 }

双向数据绑定

hook

import React, { FC, ReactNode, useContext, useEffect } from 'react'
import { FormContext } from './form'

  const { dispatch, fields, initialValues, validateField } = useContext(FormContext)
  // 获取store 对应的 value
  const fieldState = fields[name]
  const value = fieldState && fieldState.value

  const onValueUpdate = (e:any) => {
    const value = getValueFromEvent(e)
    console.log('new value', value)
    dispatch({ type: 'updateValue', name, value })
  }
  
  // 1 手动的创建一个属性列表,需要有 value 以及 onChange 属性
  const controlProps= {}
  //.           value
  controlProps[valuePropName] = value
  //          onChange
  controlProps[trigger] = onValueUpdate
 
  // 2 获取 children 数组的第一个元素
  const childList = React.Children.toArray(children)
  // 没有子组件
  if (childList.length === 0) {
    console.error('No child element found in Form.Item, please provide one form component')
  }
  // 子组件大于一个
  if (childList.length > 1) {
    console.warn('Only support one child element in Form.Item, others will be omitted')
  }
  // 不是 ReactElement 的子组件
  if (!React.isValidElement(childList[0])) {
    console.error('Child component is not a valid React Element')
  }
  const child = childList[0] as React.ReactElement
  // 3 cloneElement,混合这个child 以及 手动的属性列表
  const returnChildNode = React.cloneElement(
    child,
    { ...child.props, ...controlProps }
  )
  return (
    <div className={rowClass}>
     
      <div className='viking-form-item'>
        <div className={itemClass}>
          {returnChildNode}
        </div>
       
      </div>
    </div>
  )
}

FormItem.defaultProps = {
  valuePropName: 'value',
  trigger: 'onChange',
  validateTrigger: 'onBlur',
  getValueFromEvent: (e) => e.target.value
}
export default FormItem

class


import React, { Children } from "react";
import FieldContext from "./FieldContext";

class Field extends React.Component {
static contextType = FieldContext;//this.context获取provider值

getControlled = (childProps)=>{
  const { name } = this.props;
  const { getFieldValue, setFieldValue } = this.context
  return {
    ...childProps,
    value:getFieldValue(name),
    onChange:(event)=>{
 
       setFieldValue(name,event.target.value)
    }
  }
}
render(){
  const {children} = this.props
  return React.cloneElement(children,this.getControlled(children.props))
}
 
}
export default Field

注册Fields组件,获取rules

hook

 const {     
  
    rules,

  } = props
  
  useEffect(() => {
   
    dispatch({ type: 'addField', name, value: { label, name, value, rules: rules || [], errors: [], isValid: true }})
  }, [])

class

import React from 'react';

class FormStore{
    constructor(forceRootUpdate ){
   
        this.fieldEntities = []

    }
  
  
    registerField = (entity) => {
        this.fieldEntities.push(entity);
    };
 

    //验证的时候,获取Fields的props rules 属性
    validateFields =()=>{
        let values = this.getFieldsValue();
        let descriptor = this.fieldEntities.reduce((descriptor,entity)=>{
            let rules = entity.props.rules,name= entity.props.name;
            if(rules&&rules.length){
                let config = rules.reduce((config,rule)=>{
                    config = {...config,...rule}
                    return config
                },{})
       
       //改值的时候,调用Fields组件的强制刷新方法,更新页面
    setFieldValue = (name,value) => {//newStore里的属性赋值给this.store
        this.store[name] =value
        this.fieldEntities.forEach(({ onStoreChange }) => {
            onStoreChange();
        });
    };
   

    }); 
}


export default function useForm() {
   
 }

class Field extends React.Component {
static contextType = FieldContext;//this.context获取provider值

onStoreChange = () => {
  this.forceUpdate();
};
componentDidMount(){
//注册当前组件(this)到 store
  this.context.registerField(this)
}
}

完整代码

hook

import React, { ReactNode, createContext, forwardRef, useImperativeHandle } from 'react'
import { ValidateError } from 'async-validator'
import useStore, { FormState }from './useStore';
export type RenderProps = (form: FormState) => ReactNode
export interface FormProps {
  /**表单名称,会作为表单字段 id 前缀使用 */
  name?: string;
  /**表单默认值,只有初始化以及重置时生效 */
  initialValues?: Record<string, any>;
  children?: ReactNode | RenderProps;
  /**提交表单且数据验证成功后回调事件 */
  onFinish?: (values: Record<string, any>) => void;
  /**提交表单且数据验证失败后回调事件 */
  onFinishFailed?: (values: Record<string, any>, errors: Record<string, ValidateError[]>) => void;
}
export type IFormContext = 
  Pick<ReturnType<typeof useStore>, 'dispatch' | 'fields' | 'validateField'>
  & Pick<FormProps, 'initialValues'>
export type IFormRef = Omit<ReturnType<typeof useStore>, 'fields' | 'dispatch' | 'form'>
export const FormContext = createContext<IFormContext>({} as IFormContext)
export const Form = forwardRef<IFormRef, FormProps>((props, ref) => {
  const { name, children, initialValues, onFinish, onFinishFailed } = props
  const { form, fields, dispatch, ...restProps } = useStore(initialValues)
  const { validateField, validateAllFields } = restProps
  useImperativeHandle(ref, () => {
    return {
      ...restProps
    }
  })
  const passedContext: IFormContext = {
    dispatch,
    fields,
    initialValues,
    validateField
  }
  const submitForm = async (e: React.FormEvent<HTMLFormElement>) => {
    e.preventDefault()
    e.stopPropagation()
    const { isValid, errors, values } = await validateAllFields()
    if (isValid && onFinish) {
      onFinish(values)
    } else if(!isValid && onFinishFailed) {
      onFinishFailed(values, errors)
    }
  }
  let childrenNode: ReactNode
  if (typeof children === 'function') {
    childrenNode = children(form)
  } else {
    childrenNode = children
  }
  return (
    <form name={name} className="viking-form" onSubmit={submitForm}>
      <FormContext.Provider value={passedContext}>
        {childrenNode}
      </FormContext.Provider>
    </form>
  )
})
Form.defaultProps = {
  name: 'viking_form'
}

export default Form

import React, { FC, ReactNode, useContext, useEffect } from 'react'
import classNames from 'classnames'
import { FormContext } from './form'
import { CustomRule } from './useStore'
export type SomeRequired<T, K extends keyof T> = Required<Pick<T, K>> & Omit<T, K>
export interface FormItemProps {
  /**字段名 */
  name: string;
  /**label 标签的文本 */
  label?: string;
  children?: ReactNode;
  /**子节点的值的属性,如 checkbox 的是 'checked' */
  valuePropName?: string;
  /**设置收集字段值变更的时机 */
  trigger?: string;
  /**设置如何将 event 的值转换成字段值 */
  getValueFromEvent?: (event: any) => any;
  /**校验规则,设置字段的校验逻辑。请看 async validator 了解更多规则 */
  rules?: CustomRule[];
  /**设置字段校验的时机 */
  validateTrigger?: string;
}

export const FormItem: FC<FormItemProps> = (props) => {
  const {     
    label,
    children,
    name,
    valuePropName,
    trigger,
    getValueFromEvent,
    rules,
    validateTrigger
  } = props as SomeRequired<FormItemProps, 'getValueFromEvent' | 'trigger' | 'valuePropName' | 'validateTrigger'>
  const { dispatch, fields, initialValues, validateField } = useContext(FormContext)
  const rowClass = classNames('viking-row', {
    'viking-row-no-label': !label
  })
  useEffect(() => {
    const value = (initialValues && initialValues[name]) || ''
    dispatch({ type: 'addField', name, value: { label, name, value, rules: rules || [], errors: [], isValid: true }})
  }, [])
  // 获取store 对应的 value
  const fieldState = fields[name]
  const value = fieldState && fieldState.value
  const errors = fieldState && fieldState.errors
  const isRequired = rules?.some(rule => (typeof rule !== 'function') && rule.required)
  const hasError = errors && errors.length > 0
  const labelClass = classNames({
    'viking-form-item-required': isRequired
  })
  const itemClass = classNames('viking-form-item-control', {
    'viking-form-item-has-error': hasError
  })
  const onValueUpdate = (e:any) => {
    const value = getValueFromEvent(e)
    console.log('new value', value)
    dispatch({ type: 'updateValue', name, value })
  }
  const onValueValidate = async () => {
    await validateField(name)
  }
  // 1 手动的创建一个属性列表,需要有 value 以及 onChange 属性
  const controlProps: Record<string, any> = {}
  controlProps[valuePropName] = value
  controlProps[trigger] = onValueUpdate
  if (rules) {
    controlProps[validateTrigger] = onValueValidate
  }
  // 2 获取 children 数组的第一个元素
  const childList = React.Children.toArray(children)
  // 没有子组件
  if (childList.length === 0) {
    console.error('No child element found in Form.Item, please provide one form component')
  }
  // 子组件大于一个
  if (childList.length > 1) {
    console.warn('Only support one child element in Form.Item, others will be omitted')
  }
  // 不是 ReactElement 的子组件
  if (!React.isValidElement(childList[0])) {
    console.error('Child component is not a valid React Element')
  }
  const child = childList[0] as React.ReactElement
  // 3 cloneElement,混合这个child 以及 手动的属性列表
  const returnChildNode = React.cloneElement(
    child,
    { ...child.props, ...controlProps }
  )
  return (
    <div className={rowClass}>
      { label &&
        <div className='viking-form-item-label'>
          <label title={label} className={labelClass}>
            {label}
          </label>
        </div>
      }
      <div className='viking-form-item'>
        <div className={itemClass}>
          {returnChildNode}
        </div>
        { hasError && 
          <div className='viking-form-item-explain'>
            <span>{errors[0].message}</span>
          </div>
        }
      </div>
    </div>
  )
}

FormItem.defaultProps = {
  valuePropName: 'value',
  trigger: 'onChange',
  validateTrigger: 'onBlur',
  getValueFromEvent: (e) => e.target.value
}
export default FormItem
import { useState, useReducer } from 'react'
import Schema, { RuleItem, ValidateError } from 'async-validator';
import mapValues from 'lodash-es/mapValues'
import each from 'lodash-es/each'

export type CustomRuleFunc = ({ getFieldValue }) => RuleItem
export type CustomRule = RuleItem | CustomRuleFunc
export interface FieldDetail {
  name: string;
  value: string;
  rules: CustomRule[];
  isValid: boolean;
  errors: ValidateError[];
}

export interface FieldsState {
  [key: string]: FieldDetail
}

export interface ValidateErrorType extends Error {
  errors: ValidateError[];
  fields: Record<string, ValidateError[]>;
}
export interface FormState {
  isValid: boolean;
  isSubmitting: boolean;
  errors: Record<string, ValidateError[]>
}
export interface FieldsAction {
  type: 'addField' | 'updateValue' | 'updateValidateResult';
  name: string;
  value: any;
}
function fieldsReducer(state: FieldsState, action: FieldsAction): FieldsState {
  switch (action.type) {
    case 'addField':
      return {
        ...state,
        [action.name]: { ...action.value }
      }
    case 'updateValue':
      return {
        ...state,
        [action.name]: { ...state[action.name], value: action.value }
      }
    case 'updateValidateResult':
      const { isValid, errors } = action.value
      return {
        ...state,
        [action.name]: { ...state[action.name], isValid, errors }
      }
    default:
      return state;
  }
}
// * react hooks
// * class - ant design

function useStore(initialValues?: Record<string, any>) {
  // form state
  const [ form, setForm ] = useState<FormState>({ isValid: true, isSubmitting: false, errors: {} })
  const [ fields, dispatch ] = useReducer(fieldsReducer, {})
  const getFieldValue = (key: string) => {
    return fields[key] && fields[key].value
  }
  const getFieldsValue = () => {
    return mapValues(fields, item => item.value)
  }
  const setFieldValue = (name: string, value: any) => {
    if (fields[name]) {
      dispatch({ type: 'updateValue', name, value })
    }
  }
  const resetFields = () => {
    if (initialValues) {
      each(initialValues, (value, name) => {
        if (fields[name]) {
          dispatch({ type: 'updateValue', name, value})
        }
      })
    }
  }
  const transfromRules = (rules: CustomRule[]) => {
    return rules.map(rule => {
      if (typeof rule === 'function') {
        const calledRule = rule({ getFieldValue })
        return calledRule
      } else {
        return rule
      }
    })
  }
  const validateField = async (name: string) => {
    const { value, rules } = fields[name]
    const afterRules = transfromRules(rules)
    const descriptor = {
      [name]: afterRules
    }
    const valueMap = {
      [name]: value
    }
    const validator = new Schema(descriptor)
    let isValid = true
    let errors: ValidateError[] = []
    try {
      await validator.validate(valueMap)
    } catch (e) {
      isValid = false
      const err = e as any
      console.log('e', err.errors)
      console.log('fields', err.fields)
      errors = err.errors
    } finally {
      console.log('errors', isValid)
      dispatch({ type: 'updateValidateResult', name, value: { isValid, errors }})
    }
  }
  const validateAllFields = async () => {
    let isValid = true
    let errors: Record<string, ValidateError[]> = {}
    const valueMap = mapValues(fields, item => item.value)
    // {'username': 'abc'}
    const descriptor = mapValues(fields, item => transfromRules(item.rules))
    const validator = new Schema(descriptor)
    setForm({ ...form, isSubmitting: true })
    try {
      await validator.validate(valueMap)
    } catch(e) {
      isValid = false
      const err = e as ValidateErrorType
      errors = err.fields
      each(fields, (value, name) => {
        // errors 中有对应的 key
        if (errors[name]) {
          const itemErrors = errors[name]
          dispatch({ type: 'updateValidateResult', name, value: { isValid: false, errors: itemErrors }})
        } else if (value.rules.length > 0 && !errors[name]) {
          dispatch({ type: 'updateValidateResult', name, value: { isValid: true, errors: [] }})
        }
        //  有对应的 rules,并且没有 errors
      })
    } finally {
      setForm({ ...form, isSubmitting: false, isValid, errors })
      return {
        isValid,
        errors,
        values: valueMap
      }
    }
  }
  return {
    fields,
    dispatch,
    form,
    validateField,
    getFieldValue,
    validateAllFields,
    getFieldsValue,
    setFieldValue,
    resetFields,
  }
}

export default useStore

class

import React from "react";
import useForm from "./useForm";
import FieldContext from "./FieldContext";

const Form = ({initialValues,onFinish,onFinishFailed,children}) => {
  const [formInstance] = useForm();
  formInstance.setCallbacks({
    onFinish,
    onFinishFailed
  });
  const mountRef = React.useRef(null);
  formInstance.setInitialValues(initialValues, !mountRef.current);
  if (!mountRef.current) {
    mountRef.current = true;
  }
  return (
    <form
      onSubmit={event => {
        event.preventDefault();
        event.stopPropagation();
        formInstance.submit();
      }}>
      <FieldContext.Provider value={formInstance}>
        {children}
      </FieldContext.Provider>
    </form>
  );
}

export default Form;
import React, { Children } from "react";
import FieldContext from "./FieldContext";

class Field extends React.Component {
static contextType = FieldContext;//this.context获取provider值
componentDidMount(){
  this.context.registerField(this)
}

onStoreChange = () => {
  this.forceUpdate();
};
getControlled = (childProps)=>{
  const { name } = this.props;
  const { getFieldValue, setFieldValue } = this.context
  return {
    ...childProps,
    value:getFieldValue(name),
    onChange:(event)=>{
 
       setFieldValue(name,event.target.value)
    }
  }
}
render(){
  const {children} = this.props
  return React.cloneElement(children,this.getControlled(children.props))
}
 
}
export default Field
import React from 'react';
import AsyncValidator from './async-validator';
class FormStore{
    constructor(forceRootUpdate ){
        this.forceRootUpdate= forceRootUpdate
        this.store = {}
        this.callbacks = {}
        this.fieldEntities = []

    }
    setFieldsValue = (newStore)=>{
        this.store={...this.store,...newStore}
        this.fieldEntities.forEach(({ onStoreChange }) => {
            onStoreChange();
        });
    }
    getFieldValue =(name)=>{
        return this.store[name]
    }
    setCallbacks = (callbacks)=>{
        this.callbacks= callbacks
    }
    setInitialValues = (initialValues) => {
        this.store = { ...initialValues };
    };
    registerField = (entity) => {
        this.fieldEntities.push(entity);
    };
 
  submit = () => {
                  this.validateFields()
                  .then(values => {
                    const { onFinish } = this.callbacks;
                    if (onFinish) {
                        onFinish(values);
                    }
                  })
                  .catch(errorInfo => {
                    const { onFinishFailed } = this.callbacks;
                    if (onFinishFailed) {
                      onFinishFailed(errorInfo);
                    }
                  });
                };

    validateFields =()=>{
        let values = this.getFieldsValue();
        let descriptor = this.fieldEntities.reduce((descriptor,entity)=>{
            let rules = entity.props.rules,name= entity.props.name;
            if(rules&&rules.length){
                let config = rules.reduce((config,rule)=>{
                    config = {...config,...rule}
                    return config
                },{})
                descriptor[name] = config
            }
         
            return descriptor
        },{})
        // const descriptor = {
        //     name: {
        //       type: 'string',
        //       required: true,
        //       validator: (rule, value) => value === 'muji',
        //     }
        // }  
        return new AsyncValidator(descriptor).validate(values);
    }
    setFieldValue = (name,value) => {//newStore里的属性赋值给this.store
        this.store[name] =value
        this.fieldEntities.forEach(({ onStoreChange }) => {
            onStoreChange();
        });
    };
    getFieldsValue = () => {
        return this.store;
    }
    getForm = () => ({
        getFieldValue: this.getFieldValue,
        getFieldsValue: this.getFieldsValue,
        setFieldValue: this.setFieldValue,
        setFieldsValue:this.setFieldsValue,
        setInitialValues: this.setInitialValues,
        setCallbacks: this.setCallbacks,
        registerField: this.registerField,
        submit: this.submit,
    }); 
}


export default function useForm() {
    //多次渲染保持不变
    let formRef = React.useRef()
    //强行刷新组件方法
    let [,forceUpdate] = React.useState({})
    if(!formRef.current){
    const forceReRender = ()=>{
        forceUpdate({})
    }
    let formStore = new FormStore(forceReRender)
    let formInstance = formStore.getForm()
    formRef.current = formInstance
} 
    return [formRef.current]
 }
class AsyncValidator {
    constructor(descriptor) {
        this.descriptor = descriptor;
    }
     validate(values) {
        return new Promise(async (resolve,reject) => {
            let errorFields = [];
            for (let name in this.descriptor) {
                let rules = this.descriptor[name];
                if (rules) {
                    let ruleKeys = Object.keys(rules);
                    let errors = [];
                    for(let i=0;i<ruleKeys.length;i){
                        let ruleKey = ruleKeys[i];
                        if (ruleKey === 'required') {
                            if (rules[ruleKey] && !values[name]) {
                                errors.push(`${name} is required`);
                            }
                        } else if (ruleKey === 'type') {
                            if (typeof values[name] !== rules[ruleKey]) {
                                errors.push(`${name} is not ${rules[ruleKey]}`);
                            }
                        }else if (ruleKey === 'min') {
                            if (values[name].length <  rules[ruleKey]) {
                                errors.push(`${name} must be at least ${rules[ruleKey]} characters` );
                            }
                        }   else if (ruleKey === 'validator') {
                            let validator = rules[ruleKey];
                            let result = await validator(rules[ruleKey], values[name]);
                            if(result.length>0){
                                errors.push(`${name}不符合自定义验证器的需求!` );
                            }
                                                    }
                    }
                    if(errors && errors.length){
                        errorFields.push({name,errors});
                    }
                }
            }
            if(errorFields && errorFields.length>0){
                reject({errorFields,values});
            }else{
                resolve(values);
            }
        });
    }
}
//export default Schema;
module.exports = AsyncValidator;

验证功能参考库

[](# github.com/yiminghe/as…)

React.children

React.Children 是 React 的内置对象,具有很多方法。使用 React.Children 处理 this.props.children 可以解决数据类型的问题。this.props.children 或者 props.children 表示某个组件的子组件。注意数据类型:如果没有子组件,null;如果只有一个子组件,object;如果有多个子组件,就是 array。所以直接处理 this.props.children 比较麻烦。下面是主要方法

React.Children.map(),React.Children.forEach()、React.Children.count()、React.Children.only()、React.Children.toArray(),通常与React.cloneElement()结合使用来操作this.props.children。

React.Children.map()

  const renderChildren = () => {
    return React.Children.map(children, (child, index) => {
      const childElement = child as React.FunctionComponentElement<MenuItemProps>
      const { displayName } = childElement.type
      if (displayName === 'MenuItem' || displayName === 'SubMenu') {
        return React.cloneElement(childElement, {
          index: index.toString()
        })
      } else {
        console.error("Warning: Menu has a child which is not a MenuItem component")
      }
    })
  }
  
    <ul className={classes} style={style} data-testid="test-menu">
      <MenuContext.Provider value={passedContext}>
        {renderChildren()}
      </MenuContext.Provider>
    </ul>

React.Children.count

React.Children.count()用来计数,返回child个数。不要用children.length来计数,如果Father组件里只有'hello world!'会返回12,显然是错误的结果。

function Father (){
reurn (
<div>{
React.Children.count(children)}</div>
)
}

<Father>hellow world!</Father>

React.Children.toArray()

React.Children.count()用来计数,返回child个数。不要用children.length来计数,如果Father组件里只有'hello world!'会返回12,显然是错误的结果。

截屏2023-11-16下午8.00.39的副本.png 例二:

  <Item label='用户名' name='name' rules={[{type: 'string',required: true, min: 3}]}>
        <Input/>
 </Item>

 const childList = React.Children.toArray(children)
  const child = childList[0]as React.ReactElement
  const renderChildNode = React.cloneElement(
    child,
    {...child.props,...controlProps}
  )

AutoComplete

自定义搜索结果模版

截屏2023-12-24下午2.33.30.png

  const renderOption = (item: DataSourceType) => {
    const itemWithNumber = item as DataSourceType<LakerPlayerProps>
    return (
      <>
        <b>名字: {itemWithNumber.value}</b>
        <span>球衣号码: {itemWithNumber.number}</span>
      </>
    )
  }
  return (
    <AutoComplete
      fetchSuggestions={handleFetch}
      placeholder="输入湖人队球员英文,自定义下拉模版"
      renderOption={renderOption}
    />
  )
}
const renderTemplate = (item: DataSourceType) => {
    return renderOption ? renderOption(item) : item.value
  }


 {suggestions.map((item, index) => {
            const cnames = classNames('suggestion-item', {
              'is-active': index === highlightIndex
            })
            return (
              <li key={index} className={cnames} onClick={() => handleSelect(item)}>
                {renderTemplate(item)}
              </li>
            )
          })}
          
          

引入

import AutoComplete,{DataSourceType} from './components/AutoComplete/autoComplete'
export type DataSourceType<T = {}> = T & DataSourceObject

export const AutoComplete: FC<AutoCompleteProps> = (props) => {
}
export default AutoComplete;

ref

组件设置属性

import React, { useRef } from 'react'
const input = useRef<HTMLInputElement>(null)
  useEffect(() => {
    // focus input
    if (input.current) {
      input.current.focus()
      if (multiple && selectedValues.length > 0) {
        input.current.placeholder = ''
      } else {
        if (placeholder) input.current.placeholder = placeholder
      }
    }
  }, [selectedValues, multiple, placeholder])
  ==========================
   <Input
          ref={input}
          placeholder={placeholder} 
       
        />

获取组件宽度

 useEffect(() => {
    if (containerRef.current) {
      containerWidth.current = containerRef.current.getBoundingClientRect().width
    }
  })
  ========================
    <div className="viking-selected-tags" style={{maxWidth: containerWidth.current - 32}}> </div>

自定义选项卡

自定义图标 label 传props

<Tabs
>
  <TabItem label={<><Icon icon="check-circle" />{'  '}自定义图标</>}>
    this is card one
  </TabItem>

</Tabs>

tab box 实现显示隐藏

 const renderContent = () => {
    return React.Children.map(children, (child, index) => {
      if (index === activeIndex) {
        return child
      }
    })
  }

多个事件绑定

 const clickEvents = context.mode === 'vertical' ? {
    onClick: handleClick
  } : {}
  const hoverEvents = context.mode !== 'vertical' ? {
    onMouseEnter: (e: React.MouseEvent) => { handleMouse(e, true)},
    onMouseLeave: (e: React.MouseEvent) => { handleMouse(e, false)}
  } : {}
 <li key={index} className={classes} {...hoverEvents}>
      <div className="submenu-title" {...clickEvents}>
     
      </div>
   
    </li>

useState 异步问题

useRef

useRef进行存储数据,再使用监听数据变化,并进行更新。

截屏2023-12-26下午3.14.43.png

截屏2023-12-26下午3.15.42.png

useState

  const [ fileList, setFileList ] = useState([]);
  
  setFileList(prevList => {
          return prevList.map(file => {
            if (file.uid === updateFile.uid) {
              return { ...file, ...updateObj }
            } else {
              return file
            }
          })
        })
 setFileList(prevList => {
      return [_file, ...prevList]
    })

透传className style

截屏2024-01-01下午8.09.08.png

截屏2024-01-01下午8.09.18.png

更改对象值

{
"userName":{"label":"用户名","name":"userName","value":"1"},
"passWard":{"label":"密码","name":"passWard","value":""}
}
 switch (action.type) {
      case 'addField':
        return {
          ...state,
          [action.name]: action.value
        }
        case 'updateValue':
          debugger
      return {
        ...state,
        [action.name]: { ...state[action.name], value: action.value }
      }