Существует реакт-компонент и класс с обработчиками этого компонента, который строится на специальном абстрактном классе. Выглядит это так:
Абстрактный класс
export default abstract class HandlersCreator<C extends Component> {
constructor(public component: C) {}
protected get props(): C['props'] {
return this.component.props
}
protected get state(): C['state'] {
return this.component.state
}
protected setState<K extends keyof C['state']>(
state: Pick<C['state'], K>,
callback?: () => any
): void {
return this.component.setState(state, callback)
}
protected dispatch<A extends Action>(action: A): A {
const dispatchFunc: Dispatch<C> = (<any>this.component.props).dispatch
return dispatchFunc(action)
}
}
Класс с обработчиками
export default class Handlers extends HandlersCreator<MyComponent> {
public onClick() {
this.setState({ property: value })
}
}
Компонент
class MyComponent extends Component {
constructor() {
super()
this.handlers = new Handlers(this)
}
public render() {
return <div onClick={this.handlers.onClick}/>
}
}
Проблема заключается в том, что контекст (this) передаётся в метод «onClick» не от экземпляра класса Handlers, а от div'а, соответственно такой код выдаст исключение, мол this.setState is not a function.
Каким образом можно предустановить контекст без прямого бинда внутри onClick'а div'а? Спасибо.
UPD
В голове появляется мысль пробежаться в конструкторе абстрактного класса по методам экземпляра и пробиндить их все на this, но не знаю как это делать.
Не надо городить какие-то мутные полумиксины. Если уж есть необходимость вынесения хэндлеров в отдельный класс (переиспользование хэндлеров?), то надо, чтобы эти хэндлеры работали непосредственно с компонентом, а не выволакивали из него какие-то куски:
class HandlersCreator {
constructor(component) {
this.component = component
for (var p=this; (p=Object.getPrototypeOf(p))!==Object.prototype; ) {
for (var key of Object.getOwnPropertyNames(p)) {
if (key !== 'constructor' && typeof p[key] === 'function') {
this[key] = this[key].bind(this)
}
}
}
}
}
class Handlers extends HandlersCreator {
onClick() {
var {state} = this.component
this.component.setState({
count: state.count + 1
})
}
}
class MyComponent extends React.Component {
constructor(props) {
super(props)
this.state = { count: 0 }
this.handlers = new Handlers(this)
}
render() {
return <div onClick={this.handlers.onClick}>{this.state.count}</div>
}
}
ReactDOM.render(<MyComponent />, document.querySelector('main'))
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/15.1.0/react.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/15.1.0/react-dom.min.js"></script>
<main></main>
PS: От тайпскрипта избавился, поскольку на ответ он не влияет, а без него можно запускаемый сниппет сделать.
Пока использую вложенные функции для обработчиков событий элементов, но это не самое красивое решение
export default class Handlers extends HandlersCreator<MyComponent> {
public onClick(): eventHandler {
return (event: Event) => {
this.setState({ property: value })
}
}
}
// ...
class MyComponent extends Component {
constructor() {
super()
this.handlers = new Handlers(this)
}
public render() {
return <div onClick={this.handlers.onClick()}/>
}
}
З.Ы. Спасибо сообществу stackoverflow, что восстановили справедливость и переоткрыли вопрос
Кофе для программистов: как напиток влияет на продуктивность кодеров?
Рекламные вывески: как привлечь внимание и увеличить продажи
Стратегії та тренди в SMM - Технології, що формують майбутнє сьогодні
Выделенный сервер, что это, для чего нужен и какие характеристики важны?
Современные решения для бизнеса: как облачные и виртуальные технологии меняют рынок
Есть код, который отправляет сообщение в группу:
писал пример на Babel + JSX, при проверке в IE11 получил ошибку на array function (в IE11 её поддержки нет)