Компонент высшего порядка - это шаблон React, где:
функция берет Компонент и возвращает новый Компонент.
https://reactjs.org/docs/higher-order-components.html
Одна вещь, которую HOC может сделать для формы, это управлять состоянием, обрабатывать события, такие как onChange
и onSubmit
, и т. Д., И т. Д. Таким образом, Form
Component рассматривается как функциональный компонент, который передается как параметр вашего FormHandler
HOC.
Например,
В FormHandler.js
const withFormHandling = FormComponent => class extends Component {/* Component Logic */}
export default withFormHandling
В Form.js
import withFormHandling from './path/to/Components/FormHandler'
const Form = props => {/* Component Logic */}
export default withFormHanlding(Form);
Как тогда мы обрабатываем специфику формы, реквизиты и состояние для нескольких различных форм?
- Определите состояние или реквизиты, которые должна иметь каждая форма
В вашем случае возможно следующее:
formAction
formName
handleChange
handleSubmit
inputNames
notes
errors
Я бы рассмотрел передачу inputNames
и errors
в качестве реквизита (они должны совпадать по структуре). Вы можете добавить все виды сложности здесь или сделать его простым. Лично я держу в своем состоянии объект fields
и объект errors
, оба с соответствующими ключами. Один для сохранения введенных пользователем значений, другой для хранения результатов проверки полей.
Давайте тогда заполните наш HOC
const withFormHandling = FormComponent => class extends Component {
constructor(props) {
super(props)
this.state = {
fields: {...props.inputNames},
errors: {...props.errors},
selectedFile: null
}
this.handleChange = this.handleChange.bind(this);
this.handleFile = this.handleFile.bind(this);
this.handleSubmit = this.handleSubmit.bind(this);
}
// https://codeburst.io/save-the-zombies-how-to-add-state-and-lifecycle-methods-to-stateless-react-components-1a996513866d
static get name() {
return Component.name;
}
handleChange(e) {
const target = e.target;
let value = target.type === 'checkbox' ? target.checked : target.value;
let name = target.name;
const fields = {...this.state.fields}, errors = {...this.state.errors};
const error = //create a validation function that returns an error based on field name and value
fields[name] = value;
errors[name] = error;
this.setState({ fields, errors })
}
handleFile(e) {
this.setState({selectedFile: event.target.files[0]})
}
handleSubmit(e) {
//validate form
//flatten fields into structure of data for form submission
//handle submission of data and also file data
}
render() {
return <FormComponent
{...this.props} //to access form specific props not handled by state
fields={this.state.fields}
errors={this.state.errors}
handleChange={this.handleChange}
handleFile={this.handleFile}
handleSubmit={this.handleSubmit}
/>
}
}
export default withFormHandling
Этот шаблон работает, потому что функция рендеринга возвращенного компонента визуализирует компонент формы, переданный в качестве параметра функции HOC.
Итак, вы можете создать любое количество форм с этим HOC в качестве обработчика. Вы можете рассмотреть возможность передачи в HOC дерева, представляющего структуру ввода формы, чтобы сделать ее еще более модульной и многократно используемой.
А сейчас давайте заполним Form.js
для предоставленного вами примера:
import withFormHandling from './path/to/Components/FormHandler'
const Form = ({formAction, formName, handleChange, handleFile, handleSubmit, fields, errors, notes}) => {
return (
<form action={formAction} name={formName} onSubmit={handleSubmit}>
<div className="group">
<label htmlFor="name">Descriptive Name:</label>
<input type="text" name="name" value={fields.name}
onChange={handleChange} placeholder="Descriptive Name" />
</div>
<div className="group">
<label htmlFor="codeName">Sample Codename:</label>
<input type="text" name="codeName" value={fields.codeName}
onChange={handleChange} placeholder="Ex: MM_MG_01" />
</div>
<div className="group">
<label htmlFor="coords">GPS Coordinates:</label>
<input type="text" name="coords" value={fields.coords}
onChange={handleChange} placeholder="GPS Coordinates" />
</div>
<div className="group">
<label htmlFor="METAdesc">Metagenomic Data:</label>
<textarea type="text" name="METAdesc" value=
{fields.METAdesc}
onChange={handleChange} placeholder="Image Description" rows={7} />
<input type="file" name="METAimage"
onChange={handleFile} />
</div>
{notes}
</form>
)
}
export default withFormHanlding(Form);
Наконец, в каком-то другом компоненте вы вызываете компонент Form
так часто, как хотите, передавая уникальные реквизиты.
//...some other Component Render Method
// form one, with its own internal state managed by HOC
<Form formAction={'https://someendpoint1'} formName={"some form 1"} inputNames={{name:'', codename:''}} errors={{name:'', codename:''}} notes={"some notes 1"}/>
// form two, with its own internal state managed by HOC
<Form formAction={'https://someendpoint2'} formName={"some form 2"} inputNames={{name:'', codename:''}} errors={{name:'', codename:''}} notes={"some notes 2"}/>
// form three, with its own internal state managed by HOC
<Form formAction={'https://someendpoint3'} formName={"some form 3"} inputNames={{name:'', codename:''}} errors={{name:'', codename:''}} notes={"some notes 3"}/>
Вот так я обработал приложение с множеством различных форм, имеющих похожую структуру.
Другой шаблон, который нужно рассмотреть, - render props
, но я оставлю это на рассмотрение другого вопроса и другого респондента.