技术

onBlur、onClick、onMouseDown联动问题

一个简单有趣的交互执行顺序问题

在antd的table中有这样的场景

其中翻译文本列的内容可以双击,展开输入框,有一个这样的需求:输入框失焦时自动保存翻译好的数据

需要失焦会想到用onBlur实现,所以有了以下代码

const EditInput = React.memo<{
    value: string;
    onChange: (value: string) => void;
    onSave: () => void;
    placeholder: string;
}>(({ value, onChange, onSave, placeholder }) => {
    const inputRef = React.useRef<any>(null);
    React.useEffect(() => {
        if (inputRef.current) {
            try {
                inputRef.current.focus?.({ cursor: 'all' });
            } catch (_) {
                inputRef.current?.input?.select?.();
            }
        }
    }, []);

    return (
        <Input
            ref={inputRef}
            value={value}
            onChange={(e) => onChange(e.target.value)}
            placeholder={placeholder}
            autoFocus
            onBlur={onSave}
            onPressEnter={onSave}
        />
    );
});

如果用户不想保存怎么办?

那得点击操作列的取消按钮,取消按钮的逻辑是onCancel,也就是取消这次操作,取消按钮只有在input被编辑状态才会出现

如果给取消按钮绑定onClick,那么输入框的onBlur事件会优先触发(优先级问题),代码中的onBlur事件绑定了onSave,也就是说会触发保存功能,那么取消按钮绑定的onClick事件就变成摆设了 😗

<Text
    className={styles.actionText}
    style={{ color: '#1890FF' }}
    onClick={() => handleSave(record)}
>
    {isSaving ? translatedTexts.saving : translatedTexts.save}
</Text>
<Text
    className={styles.actionText}
    style={{ color: '#999' }}
    onMouseDown={(e) => {
        // mousedown 先于 blur 触发,提前标记取消,防止 onBlur 触发 onSave逻辑
        handleCancelEdit(record.id);
    }}
>
    {translatedTexts.cancel}
</Text>

如何避免这个问题?

改变点击事件的执行顺序,将取消按钮的onClick事件变更为onMouseDown,就可以避免取消按钮不生效的问题

但是在这个业务场景中,取消逻辑执行后,自动翻译按钮会取代取消按钮的位置,在MouseUp期间,自动翻译按钮的onClick逻辑也会被触发!也就是说取消逻辑后,又触发了一次自动翻译逻辑 😟

可以采用同样的处理方法,把自动翻译按钮的点击事件也修改为onMouseDown

那么在自动翻译按钮的MouseUp期间呢?因为自动翻译逻辑中包含接口的请求,此时前端进行了保护性措施,无法点击操作列的内容,并且取消按钮中没有绑定MouseUp事件,这样事件冲突就基本解决了 🥳

用户操作优化可能性

上述中有提到,只有在点击取消按钮时才能取消,应该如何优化用户体验呢?

考虑到用户可能只是双击了翻译文本想看看效果,并没有对文本内容进行任何修改,无论怎样,onBlur处的逻辑都是会触发的(onSave,包括点击保存按钮),所以对于没有修改的内容(或者就是空白的内容),逻辑判断需要在onSave中处理,如果有需求的话,可以这样实现