У меня есть вопрос о наследовании и композиции в React.
Я привык к Backbone, где обычной процедурой является создание представлений классов, которые обрабатывают логику для данных и рендеринга. Когда у вас есть некоторые компоненты, которые разделяют часть этой логики - например, формы - вы можете абстрагировать ее, создав новый компонент (класс), добавить туда разделяемую логику, а затем расширить этот класс из других компонентов. Таким образом, можно получить абстрактный компонент с этой общей логикой и расширяющими ее компонентами:
var generalForm = Backbone.View.extend({
customMethod: function (){
// Some shared logic
}
});
var Form = generalForm.extend({
// This class has customMethod from generalForm.
});
Теперь я вижу, что React работает с композицией, а не наследованием. И есть два способа сделать композицию: обернуть компоненты или использовать Компоненты высшего порядка . Чтобы упростить задачу, я сделал Песочницу с этими двумя возможностями: https://codesandbox.io/s/wyn629rm4l.
Первый способ создания композиции - завернуть компонент в другой:
class Form extends Component {
constructor(props) {
super(props);
this.state = {};
}
handleUpdate(e) {
this.setState({
[e.target.name]: e.target.value
});
}
render() {
return (
<div className="Form">
<input
type="text"
name="name"
onChange={e => {
this.handleUpdate(e);
}}
/>
</div>
);
}
}
class GeneralForm extends Component {
constructor(props) {
super(props);
}
render() {
return <form className="GeneralForm">{this.props.children}</form>;
}
}
class App extends Component {
render() {
return (
<GeneralForm>
<Form />
</GeneralForm>
);
}
}
Первый вопрос: существует ли элегантный и эффективный способ отправки логики из дочернего компонента (Form
) в компонент-оболочку (GeneralForm
), такой как handleUpdate
function?
Другой способ создания композиции - с использованием компонентов высшего порядка. Мы создаем функцию, которая возвращает компонент с некоторой логикой. Затем мы передаем компонент этой функции, и результатом будет компонент с логикой, установленной функцией. Таким образом, мы можем создавать компоненты, которые разделяют логику.
const CustomFormHoc = WrappedComponent => {
return class CustomForm extends Component {
constructor(props) {
super(props);
this.state = {};
this.handleUpdate = this.handleUpdate.bind(this);
this.submitForm = this.submitForm.bind(this);
}
submitForm(e) {
e.preventDefault();
alert(JSON.stringify(this.state, null, 2));
}
handleUpdate(e) {
this.setState({
[e.target.name]: e.target.value
});
}
render() {
return (
<WrappedComponent
{...this.props}
handleUpdate={this.handleUpdate}
submitForm={this.submitForm}
/>
);
}
};
};
class Form extends Component {
render() {
return (
<div className="Form">
<div>Form</div>
<div className="Form">
<input
type="text"
name="Address"
placeholder="Address"
onChange={e => {
this.props.handleUpdate(e);
}}
/>
<input
type="text"
name="Postal code"
placeholder="Postal code"
onChange={e => {
this.props.handleUpdate(e);
}}
/>
<input
type="submit"
onClick={e => {
this.props.submitForm(e);
}}
/>
</div>
</div>
);
}
}
let ComposedForm = CustomFormHoc(Form);
class App extends Component {
render() {
return (
<div>
<ComposedForm />
</div>
);
}
}
Это работает, но вот мой второй вопрос: есть ли способ избежать передачи handleUpdate и submitForm в WrappedComponent
? В примере высшего порядка они опущены.
<WrappedComponent
{...this.props}
handleUpdate={this.handleUpdate}
submitForm={this.submitForm}
/>
И последний вопрос: какой из этих двух способов создания композиции лучше и для каких случаев?
Спасибо!