如何解决在React中使用HOC创建受控输入
我试图创建一个HOC来呈现受控的输入,但是我的所有努力都导致onChange之后输入的焦点下降。我以为这是密钥的基本问题,但是摆弄密钥似乎无济于事,我花了几个小时进入各种“带有输入的HOC”博客/示例,似乎无法从父节点的输入中找到任何具有受控输入的东西。州。我究竟做错了什么?预先感谢!
编辑:HOC的目标是获取验证钩子。添加其他代码后,我为此问题创建了sandbox,希望能对您有所帮助。
type WrapperProps = {
name: string;
label: string;
onChange?: (event: any) => void;
state: any;
}
export const InputWrapper = (v: ValidationObject) => {
const Wrapper: FC<WrapperProps> = (props) => {
const { label,onChange,name,state } = props;
const getPattern = (value: any) => {
return v.getFieldValid(name)
? `${value}`
: `${randomString()}`
};
const modifiedProps = {
name,onBlur: () => v.validate(name,prop(name,state),pattern: getPattern(state[name]),value: state[name],};
return (
<React.Fragment>
<label htmlFor={name}>{label}</label>
<input key={name} id={name} {...modifiedProps} />
<p style={{ color: 'red' }}>{v.getError(name)}</p>
</React.Fragment>
);
}
return Wrapper;
}
我的验证钩,它将导出HOC的参数:
validation.hook.ts
import { useState } from 'react';
import { prop,map,all,indexOf,mergeDeepRight } from 'ramda';
import { createValidationsstate,compose,isEqual } from 'util/utilities';
import { InputWrapper } from './wrapper2';
export interface ValidationArray<T> {
key: keyof T;
value: unkNown;
}
export interface ErrorMessages {
[key: string]: string;
}
export interface ValidationFunction {
(val: any,state: any): boolean | string | number;
}
// Dictionary of Booleans
export interface ValidationState {
[key: string]: {
isValid: boolean;
error: string;
};
}
// Dictionary of validation deFinitions
export interface ValidationProps {
errorMessage: string;
validation: ValidationFunction;
}
export interface ValidationSchema {
[key: string]: ValidationProps[];
}
export interface ValidationObject {
getError: Function;
getFieldValid: Function;
isValid: boolean;
validate: Function;
validateall: Function;
validateIfTrue: Function;
validationState: ValidationState;
}
/**
* A hook that can be used to generate an object containing functions and
* properties pertaining to the validation state provided.
* @param validationSchema an object containing all the properties you want to validate
* @returns object { getError,getFieldValid,isValid,validate,validateall,validateIfTrue,validationState }
*/
export const useValidation = <S>(validationSchema: ValidationSchema) => {
const [isValid,setIsValid] = useState<boolean>(true);
const [validationState,setValidationState] = useState<ValidationState>(
createValidationsstate(validationSchema)
);
/**
* Executes the value against all provided validation functions and
* updates the state.
* @param key string the name of the property being validated
* @param value any the value to be tested for validation
* @return true/false validation
*/
const runAllValidators = (key: string,value: any,state?: S) => {
const runValidator = compose(
(func: Function) => func(value,prop('validation')
);
const bools: boolean[] = map(runValidator,validationSchema[key]);
const isValid: boolean = all(isEqual(true),bools);
const index: number = indexOf(false,bools);
const error = index > -1 ? validationSchema[key][index].errorMessage : '';
const validations: any = {};
validations[key] = { isValid,error };
return validations;
}
/**
* executes a validation function on a value and updates isValid state
* @param key string the name of the property being validated
* @param value any the value to be tested for validation
* @return true/false validation
*/
const validate = (key: string,state?: S) => {
if (key in validationSchema) {
const validations = runAllValidators(key,value,state);
setValidationState(mergeDeepRight(validationState,validations));
setIsValid(validations[key].isValid);
return validations[key].isValid;
}
};
/**
* updates isValid state if validation succeeds
* @param key string the name of the property being validated
* @param value any the value to be tested for validation
* @return void
*/
const validateIfTrue = (key: string,value: unkNown,state);
if (validations[key].isValid) {
setValidationState(mergeDeepRight(validationState,validations));
}
}
};
/**
* Runs all validations against an object with all values and updates/returns
* isValid state.
* @param state any an object that contains all values to be validated
* @return boolean isValid state
*/
const validateall = (state: S) => {
const bools = map((key: string) => {
return validate(key,state[key as keyof S],state);
},Object.keys(validationSchema));
const result = all(isEqual(true),bools);
setIsValid(result);
return result;
};
/**
* Get the current error stored for a property on the validation object.
* @param key the name of the property to retrieve
* @return string
*/
const getError = (key: string) => {
if (key in validationSchema) {
const val = compose(
prop('error'),prop(key),);
return val(validationState);
}
return '';
};
/**
* Get the current valid state stored for a property on the validation object.
* @param key the name of the property to retrieve
* @return boolean
*/
const getFieldValid = (key: string) => {
if (key in validationSchema) {
const val = compose(
prop('isValid'),);
return val(validationState);
}
return true;
};
const validationObject = {
getError,validationState,}
// inititally where I wanted to use the HOC and make it one
// of the available exports
const ValidationWrap = InputWrapper(validationObject);
return {
...validationObject,ValidationWrap
};
};
BasicInput.validation.ts
import {useValidation} from 'validation.hook';
export interface Dog {
name: string;
breed: string;
}
export const BasicInputValidation = () => {
return useValidation<Dog>({
name: [
{
errorMessage: 'Cannot be Bob.',validation: (val: string,state: any) => {
return val.trim().toLowerCase() !== 'bob';
}
},{
errorMessage: 'Cannot be Ross.',state: any) => {
return val.trim().toLowerCase() !== 'ross';
}
},{
errorMessage: 'Name is required.',state: any) => {
return val.trim().length > 0;
}
},],breed: [
{
errorMessage: 'Must be a Leonberger.',state: any) => {
return val.trim().toLowerCase() === 'leonberger';
}
},{
errorMessage: 'Breed is required.',]
});
};
utilities.ts
(createValidationState是运行代码所必需的,其他地方是为了方便复制意大利面
/**
* Creates a random 7 character string.
* @return string
*/
export const randomString = () => Math.random().toString(36).substring(7);
/**
* Compose function that is a little more friendly to use with typescript.
* @param fns any number of comma-separated functions
* @return new function
*/
export const compose = (...fns: Function[]) => (x: any) =>
fns.reduceRight((y: any,f: any) => f(y),x);
// Build Validation State Object
export const createValidationsstate = (schema: ValidationSchema) => {
const keys = Object.keys(schema);
return keys.reduce(
(prev: any,item: string) => {
prev[item] = {
isValid: true,error: ''
};
return prev;
},{}
);
};
以下是一个使用中的示例:
import React,{ useState } from 'react';
import {BasicInputValidation} from 'examples/basicInput.validation';
import {InputWrapper} from 'withValidationComponent';
import { curry } from 'ramda';
function App() {
const [state,setState] = useState<{name: string}>({ name: '' });
const onChange = curry((name: string,event: any) => {
const data = { [name]: event.target.value }
setState({ ...state,...data });
})
const v = BasicInputValidation();
const HOC = InputWrapper(v);
return (
<>
<HOC
name="name"
label="Name"
onChange={onChange('name')}
state={state}
/>
</>
);
}
export default App;
解决方法
每个渲染似乎都导致原始InputWrapper
组件被卸载。此时,是由于此代码const HOC = InputWrapper({});
-每次在父对象上进行渲染时,都会生成一个新的包装器。这在我的实验中很明显:
export const InputWrapper = (v) => {
const Wrapper = (props) => {
const { label,onChange,name,state } = props;
useEffect(() => {
return () => {
console.log("unmounting"); // cleanup got invoked everytime I typed in the input
};
},[]);
错误的CodeSandBox:https://codesandbox.io/s/react-input-hoc-bugged-e28iz?file=/src/withValidationComponent.js
为解决此问题,在实现方面(即在App组件上),我将包装器实例移到了函数外部
import React,{ useState } from "react";
import {BasicInputValidation} from 'examples/basicInput.validation';
import {InputWrapper} from 'withValidationComponent';
import { curry } from 'ramda';
const HOC = InputWrapper({}); // <-- moved this here
function App() {
...
版权声明:本文内容由互联网用户自发贡献,该文观点与技术仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 dio@foxmail.com 举报,一经查实,本站将立刻删除。