这是现代动态解决方案,它通过根据 json 文件重用带有 React Hooks 的 Input 组件来工作。
这是它的外观:
使用这种范例的好处:输入组件(具有自己的钩子状态)可以在任何其他应用程序部分中重用,而无需更改任何代码行。
缺点要复杂得多。
这里是简化的 json(构建组件的基础):
{
"fields": [
{
"id": "titleDescription",
"label": "Description",
"template": [
{
"input": {
"required": "true",
"type": "text",
"disabled": "false",
"name": "Item Description",
"value": "",
"defaultValue": "a default description",
"placeholder": "write your initail description",
"pattern": "[A-Za-z]{3}"
}
}
]
},
{
"id": "requestedDate",
"label": "Requested Date",
"template": [
{
"input": {
"type": "date",
"name": "Item Description",
"value": "10-14-2007"
}
}
]
},
{
"id": "tieLine",
"label": "Tie Line #",
"template": [
{
"select": {
"required": true,
"styles": ""
},
"options": [
"TL625B",
"TL626B-$selected",
"TL627B",
"TL628B"
]
}
]
}
]
}
无状态输入组件,带有 Hooks,可以读取不同的输入类型,例如:文本、数字、日期、密码等。
import React, { forwardRef } from 'react';
import useInputState from '../Hooks/InputStateHolder';
const Input = ({ parsedConfig, className }, ref) => {
const inputState = useInputState(parsedConfig);
return (
<input
//the reference to return to parent
ref={ref}
//we pass through the input attributes and rewrite the boolean attrs
{...inputState.config.attrs}
required={inputState.parseAttributeValue(inputState.config, 'required')}
disabled={inputState.parseAttributeValue(inputState.config, 'disabled')}
className={`m-1 p-1 border bd-light rounded custom-height ${className}`}
onChange={inputState.onChange}
/>
)
};
//we connect this separated component to passing ref
export default forwardRef(Input)
挂钩架 InputStateHolder.js 文件
import { useState } from 'react';
const useInputState = (initialValue) => {
//it stores read the json, proccess it,
//applies modifies and stores input values
const [config, setInputConfig] = useState({
isLoaded: false,
attrs: { ...initialValue }
});
//mutating and storing input values
function changeValue(e) {
const updatedConfig = { ...config };
updatedConfig.attrs.value = e.target.value;
setInputConfig({ ...config })
}
// to apply form configs to input element
//only one time at the first load
function checkTheFirstLoad() {
const updatedConfig = { ...config };
if (config.attrs.value.length === 0) {
updatedConfig.attrs.value = config.attrs.defaultValue;
//defaultValue is not allowed to pass as attribute in React
//so we apply its value depending on the conditions and remove it
delete updatedConfig.attrs.defaultValue;
updatedConfig.isLoaded = true;
setInputConfig(updatedConfig);
}
}
//parsing boolean input attributs such as required or disabled
function parseAttributeValue(newState, attribute) {
return typeof newState.attrs[attribute] === 'string' && newState.attrs[attribute] === 'true'
? true : false
}
!config.isLoaded && checkTheFirstLoad();
//returning the hook storage
return {
config,
onChange: changeValue,
parseAttributeValue
}
}
export default useInputState;
以及父 FormFields 组件(包含表单和提交标签):
import React, { createElement } from "react";
import Input from '../UI/Input';
const FormField = ({ setConfig }) => {
//it receives the parsed json and check to not be empty
if (!!Object.keys(setConfig).length) {
const fieldsConfig = setConfig.fields;
//the array to get created elements in
const fieldsToGetBuilt = [];
// the array to store input refs for created elements
const inputRefs = [];
// the function to store new ref
const setRef = (ref) => inputRefs.push(ref);
fieldsConfig.map(field => {
switch (true) {
//here is we create children depending on the form configs
case (!!field.template[0].input): {
let classes = 'someStyle';
fieldsToGetBuilt.push(
createElement(Input, {
ref: setRef,
parsedConfig: field.template[0].input,
key: field.id,
className: classes
})
);
break
}
//default case needed to build warning div notifying the missed tag
default: {
let classes = 'someOther danger style';
let child = `<${Object.keys(field.template[0])[0]}/> not built`;
fieldsToGetBuilt.push(
createElement('div', {
key: field.id,
className: classes
}, child)
);
}
}
})
const onSubmitHandler = (e) => {
//every time we click on submit button
//we receive the inputs`es values in console
e.preventDefault();
inputRefs.map(e =>
console.log(e.value)
)
}
return (
<div className='m-2 d-flex flex-column'>
<form onSubmit={onSubmitHandler}>
<h5 className='text-center'>{setConfig.title}</h5>
<div className='d-flex flex-row justify-content-center align-items-center'>
{fieldsToGetBuilt.map(e => e)}
</div>
<input type="submit" onClick={onSubmitHandler} className='btn-info' />
</form>
</div >
)
}
// if in json there are no any fields to get built
else return <div>no Page has been built</div>
};
export default FormField;
结果在这里
以及我们在输入字段更改并单击提交按钮后在控制台中看到的内容
PS 在我的另一个answer 我实现了基于json 的动态模块上传