我试图做一个按钮,当发生某些事情时(onclick,http成功/错误)动画标签。但是我不知道如何应用这些类并找到一种优雅的方式来通过组件状态处理它。
单击按钮时,它会从IDLE
状态过渡到BUSY
状态。当承诺解析(SUCCESS
)或拒绝(FAILURE
)。3 秒后,它重置回 IDLE
.
我正在尝试根据状态切换类名。但它不起作用,我不知道如何正确地做到这一点。我不确定我是否(以及如何)应该使用react-transition-group
插件来顺利完成此操作,因为组件没有有条件地挂载/卸载。
也许我应该使用 css 动画而不是 css 过渡。这会是更好的方法吗?
这里活生生的例子:https://codesandbox.io/s/yjl5o5vr4v
这是我的代码:
SubmitButton.tsx
import React from 'react'
import { FontAwesomeIcon } from '@fortawesome/react-fontawesome'
import { IconProp } from '@fortawesome/fontawesome-svg-core'
import classNames from 'classnames'
import './SubmitButton.css'
// flag to switch whether dummy request should succeed or fail
let workSuccess = true
interface IProps {}
interface IState {
status: 'IDLE' | 'BUSY' | 'SUCCESS' | 'FAILURE'
label: string
icon?: IconProp
}
const initialState: IState = {
status: 'IDLE',
label: 'Click',
icon: undefined,
}
class SubmitButton extends React.PureComponent<IProps, IState> {
public state: IState = { ...initialState }
public render() {
const { label, icon, status } = this.state
return (
<button className="SubmitButton" onClick={this.handleClick}>
<span
className={classNames({
'animation--after': status === 'BUSY',
'animation--before': status === 'SUCCESS' || status === 'FAILURE',
['animation']: status === 'IDLE',
})}
>
{icon && (
<FontAwesomeIcon className="SubmitButton__icon" icon={icon} />
)}
{label}
</span>
</button>
)
}
private handleClick = async (e: React.MouseEvent<HTMLButtonElement>) => {
const { status } = this.state
if (status === 'IDLE') {
// animation: 'roll up' (out of view)
// animation: reset position
this.setState({
label: 'Loading...',
icon: 'spinner',
status: 'BUSY',
})
// animation: 'roll up' (into view)
try {
const response = await this.doWork()
// animation: 'roll up' (out of view)
if (response) {
// animation: reset position
this.setState({
label: 'OK',
icon: 'check',
status: 'SUCCESS',
})
// animation: 'roll up' (into view)
}
} catch (err) {
// animation: reset position
this.setState({
label: 'ERROR',
icon: 'exclamation',
status: 'FAILURE',
})
// animation: 'roll up' (into view)
} finally {
// animation: reset position
this.setState({ ...initialState })
// animation: 'roll up' (into view)
}
}
}
private doWork = () => {
return new Promise<boolean>((resolve: any, reject: any) => {
setTimeout(() => {
if (workSuccess) {
resolve(true)
} else {
reject('An error happened...')
}
workSuccess = !workSuccess
}, 3000)
})
}
}
export default SubmitButton
提交按钮.css
.SubmitButton {
overflow: hidden;
cursor: pointer;
outline: none;
font-size: 16px;
padding: 12px 32px;
}
.SubmitButton__icon {
margin-right: 12px;
}
.animation {
display: inline-block;
transition: transform 300ms ease-out;
transform: translateY(0);
}
.animation--before {
transition: transform 300ms ease-out;
transform: translateY(50px);
}
.animation--after {
transition: transform 300ms ease-out;
transform: translateY(-50px);
}
您可以使用gif图像而不是FontAwesome图标。 请检查下面的代码。
目前我使用了低质量的 gif 作为目的,但您可以从您的项目中加载一个好的 gif。
class SubmitButton extends React.PureComponent<IProps, IState> {
public state: IState = { ...initialState };
public render() {
const { label, icon, status } = this.state;
return (
<>
<button className="SubmitButton" onClick={this.handleClick}>
<span
className={classNames({
"animation--after": status === "BUSY",
"animation--before": status === "SUCCESS" || status === "FAILURE",
["animation"]: status === "IDLE"
})}
>
{icon && (
<img src="http://www.ajaxload.info/cache/FF/FF/FF/00/00/00/1-0.gif" />
)}
{label}
</span>
</button>
</>
);
}
演示