前言
本文是对近期使用 React 技术栈在开发页面时,常见场景实现点上的一些总结,故编写此文分享给大家。关于文中功能代码在实现上,若读者有更好的想法,欢迎文末下方留言交流。
JSX 中使用 switch case 做条件渲染
对于简单的条件渲染,相信直接使用三目运算符(条件 ? 满足逻辑 : 不满足逻辑
)或与运算符(&&
)就能够解决;但对于同一个条件需要处理多种不同的形态,switch case
将是一个不错的选择。
<div>
{(() => {
switch (file.type) {
case 'jpg':
case 'jpeg':
case 'png':
case 'gif':
return '图片';
case 'word':
return 'word 文档';
case 'xlsx':
return 'xlsx 文件';
case 'pdf':
return 'pdf 文件';
default:
return null;
}
}
)()}
</div>
复制代码
input 控件做防抖处理
相信大家都会遇到这个场景:一个搜索类的 input 控件,为避免每次连续的搜索都调用 API 查询数据,我们会采用防抖来控制请求时机。
在 React 中可以通过 value 和 onChange 来将 input 变为受控元素,为了同步视图上的 value 值显示,所以防抖不会对更新 value 值做处理,而是作用于触发请求 API 的逻辑。
我们可以自定义 useDebounce
来处理请求 API 逻辑。
// 自定义防抖 Hook:useDebounce
import React from 'react';
function useDebounce(fn: Function, delay: number, flag?: boolean) {
const { current } = React.useRef({ fn, timer: undefined });
// 关键 - 目的是 fn 变化后更新 fn,才能在 fn 中拿到更新后的 input value
React.useEffect(function () {
current.fn = fn;
}, [fn]);
return React.useCallback((...args: any[]) => {
clearTimeout(current.timer);
if (flag && !current.timer) {
current.fn.apply(this, args);
}
current.timer = window.setTimeout(() => {
current.fn.apply(this, args);
}, delay);
}, []);
}
export default useDebounce;
复制代码
有了 useDebounce
,我们可以这样使用:
<input type="text" value={inputVal} onChange={handleChangeInput} />
const [inputVal, setInputVal] = useState<string>('');
const handleChangeInput = (e: ChangeEvent<HTMLInputElement>) => {
const value = e.target.value;
setInputVal(value); // 更新 value
fetchApiFn(); // 经过 useDebounce 处理过
};
const fetchApiFn = useDebounce(() => {
console.log('请求 API!', inputVal);
}, 300);
复制代码
input 输入中文拼音意外触发 onChange 事件
在 input 输入框输入中文拼音时,会意外触发 onChange 事件的执行,进而触发请求 API 逻辑;但这显然不是我们想要看到的,我们期望用户输入中文拼音并选择了中文汉字后,再进行 API 操作。
可以借助于 input 元素上 onCompositionStart,onCompositionEnd
事件来完成,还是上面的例子,代码如下:
// Input 中文搜索锁(变量定义在函数组件外层,或在函数内部使用 useRef 保存)
let compositionLock = false;
<input
type="text"
value={inputVal}
onCompositionStart={handleComposition}
onCompositionEnd={handleComposition}
onChange={handleChangeInput} />
const handleChangeInput = (e: ChangeEvent<HTMLInputElement>) => {
const value = e.target.value;
setInputVal(value); // 更新 value
if (compositionLock) return; // 拼音还没有输入完,不进入下面接口 API 操作
fetchApiFn(); // 经过 useDebounce 处理过
};
const handleComposition = (event: CompositionEvent<HTMLInputElement> ) => {
if (event.type === 'compositionend') {
compositionLock = false;
handleChangeInput(event as any); // 选择汉字后
} else {
compositionLock = true; // onCompositionStart 输入拼音开始时
}
}
复制代码
ref 的特殊场景使用
对于函数组件,它和 JS 普通函数无异,执行函数拿到返回结果,函数实体消失;而 class 类组件本身是一个类对象,存在实体所以可以直接在 class 组件上使用 ref 属性。
若想在函数组件中使用 ref,可以通过 React.forwardRef
转发 ref 来实现。
场景一:ref 作用于元素
绑定函数组件内的某个元素,提供给外部去使用。
import React from 'react';
const ParentFn = () => {
const inputRef = React.useRef(null);
const handleBtnClick = () => {
console.log(inputRef.current);
}
return (
<div>
<button onClick={handleBtnClick}>访问 child input 元素</button>
<ChildFn ref={inputRef} />
</div>
)
}
const ChildFn = React.forwardRef((props, ref) => {
return <input type="text" ref={ref} />
});
复制代码
场景二:ref 作用于持久化数据
比如在上面我们处理中文拼音时,在函数组件外层定义了一个变量:compositionLock
,我们换一种方式,通过 ref 来定义此变量。
我们知道函数组件每次更新时,都会将函数进行 () 调用,这就导致内部的变量都会重新被创建,导致更新前的变量值被重置,那如何实现持久化缓存变量值呢?ref
可以帮助我们实现。
const compositionLockRef = useRef(false);
const handleComposition = (event: CompositionEvent<HTMLInputElement> ) => {
if (event.type === 'compositionend') {
compositionLockRef.current = false;
handleChangeInput(event as any); // 选择汉字后
} else {
compositionLockRef.current = true; // onCompositionStart 输入拼音开始时
}
}
复制代码
需要注意的是:useRef
会返回一个具有 current
属性指针的对象,current
上存储的才是我们定义的变量值。
场景三:ref 向外暴露组件内部属性、方法
比如在某些场景下,子组件需要对外暴露一些特定的处理函数,在父组件做完某件事情后来调用处理函数完成一些功能,那子组件如何对外暴露呢?
借助于 React Hooks useImperativeHandle
来实现:
import React from 'react';
// ParentFn
const ChildFn = React.forwardRef((props, ref) => {
React.useImperativeHandle(ref, () => ({
onKeyDown: (event) => { // ... },
onKeyUp: () => { //... },
scrollTo: (index) => { // ... },
}));
return <input type="text" />
});
复制代码
保留字符串值当中的标签符号
比如有一个场景需要根据搜索关键字,对搜索列表中匹配的字符进行高亮(更改匹配的字符颜色),于是我们很快写出了转换函数:
const highlightKeyword = (value: string, text: string) => {
if (!text) return text;
return text.replace(
new RegExp(value, 'g'),
`<span style="color: #3297FC;">${value}</span>`
);
}
复制代码
此时回过头看页面效果,发现 <span style="color: #3297FC;">
按原模原样的文本渲染到页面了,但我们期望的是 <span>
能被作为 HTML 元素去使用。
好在 React 提供了一个属性 dangerouslySetInnerHTML
来做这件事情,类似于 Vue 的 v-html 指令:
<span dangerouslySetInnerHTML={{ __html: highlightKeyword(text) }}></span>
复制代码
文末
本文在编写中如有不足的地方,?欢迎读者提出宝贵意见,作者进行改正。