分页
思路
左边隐藏:currentPage> middleValue+1
右面隐藏:currentPage< total -middleValue
一共能显示7个按钮。
middleValue = 7/3
case1:左边不隐藏,右面隐藏
case2:左右隐藏
case3:左面隐藏,右面不隐藏
日历组件
实现日历面板
思路:直接将自己向前移动多少天后,开始循环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 组件
类方法实现图
函数组件实现图
调用
<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,显然是错误的结果。
例二:
<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
自定义搜索结果模版
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进行存储数据,再使用监听数据变化,并进行更新。
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
更改对象值
{
"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 }
}