我在不同的项目中使用React和Aurelia。
我的Aurelia应用程序变得非常复杂,我真的想要处理重复的逻辑。假设我想要一个限制异常请求可以再次点击的限制按钮,使用React非常简单:
我希望我的<ThrottledButton />
与原生<button />
具有完全相同的行为,除非它会自动阻止多个请求发生,直到上一个请求完成或失败:
class MyComponent extends Component {
handleClick = async e => {
const response = await this.longRequest(e)
console.log(`Received: `, response)
}
longRequest = async e => {
return new Promise((res, rej) => {
setTimeout(() => { res(e) }, 1000)
})
}
render() {
return <div>
<button className="btn" onClick={this.handleClick}>Click me</button>
<ThrottledButton className="btn" onClick={this.handleClick}>Click me</ThrottledButton>
</div>
}
}
以下是<ThrottledButton />
:
class ThrottledButton extends Component {
constructor(props) {
super(props)
this.state = { operating: false }
}
render() {
const { onClick, ...restProps } = this.props
const decoratedOnClick = async e => {
if (this.state.operating) return
this.setState({ operating: true })
try {
await Promise.resolve(onClick(e))
this.setState({ operating: false })
} catch (err) {
this.setState({ operating: false })
throw err
}
}
return <button onClick={decoratedOnClick} {...restProps}>{this.props.children}</button>
}
}
现在我真的想在Aurelia中实现相同(或类似)的东西,这里是用例组件:
<template>
<require from="components/throttled-button"></require>
<button
class="btn"
click.delegate="handleClick($event)">
Click me
</button>
<throttled-button
class="btn"
on-click.bind="handleClick">
Click me
</throttled-button>
</template>
export class MyComponent {
handleClick = async e => {
const response = await this.longRequest(e)
console.log(`Received: `, response)
}
async longRequest(e) {
return new Promise((res, rej) => {
setTimeout(() => { res(e) }, 1000)
})
}
}
这是我目前拥有的限制按钮:
<template>
<button click.delegate="decoratedClick($event)" class.bind="class">
<slot></slot>
</button>
</template>
export class ThrottledButton {
@bindable onClick = () => {}
@bindable class
constructor() {
this.operating = false
}
decoratedClick = async e => {
console.log(this.operating)
if (this.operating) return
this.operating = true
try {
await Promise.resolve(this.onClick(e))
this.operating = false
} catch (err) {
this.operating = false
throw err
}
}
}
如您所见,<throttled-button />
与原生<button />
完全不同,尤其是:
而不是类似于React的<button {...props} />
点差
运算符,我必须手动传递我绑定的每个属性
到<throttled-button />
到原生按钮。对于大多数组件来说,它是一个
巨大的努力,可能会导致许多错误。
我必须绑定(或调用)on-click属性,这使得它与本地属性的工作方式完全不同。为了解决这个问题,我 尝试使用DOM dispatchEvent API
这是我试过的:
<template>
<button ref="btn">
<slot></slot>
</button>
</template>
@inject(Element)
export class ThrottledButton {
constructor(el) {
this.el = el
this.operating = false
}
attached = () => {
this.btn.addEventListener('click', this.forwardEvent)
}
detached = () => {
this.btn.removeEventListener('click', this.forwardEvent)
}
forwardEvent = e => {
this.operating = true
this.el.dispatchEvent(e) // no way to get the promise
}
}
但是,没有办法获得对promise的引用,这使得限制成为不可能。
有没有希望在Aurelia解决这些问题?任何想法都将不胜感激。
答案 0 :(得分:2)
您可以使用自定义属性来解决问题。这是打字稿中的代码。
3
/ \
3 3
/ \ / \
1 2 3 4
并像这样使用它。
import { autoinject, customAttribute } from 'aurelia-framework';
@customAttribute('single-click')
@autoinject()
export class SingleClickCustomAttribute {
clicked: (e: Event) => void;
private value: () => Promise<any>;
private executing: boolean = false;
constructor(
private element: Element
) {
this.clicked = e => {
if (this.executing) {
return;
}
this.executing = true;
setTimeout(async () => {
try {
await this.value();
}
catch (ex) {
}
this.executing = false;
}, 100);
};
}
attached() {
this.element.addEventListener('click', this.clicked);
}
detached() {
this.element.removeEventListener('click', this.clicked);
}
}
要调用的异步方法应该是这样的:
<button single-click.call="someAsyncMethod()" type="button">...</button>
诀窍是使用.call传递异步函数并使用状态属性。