React 技术栈开发总结 一

前言

本文是对近期使用 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>
复制代码

文末

本文在编写中如有不足的地方,?欢迎读者提出宝贵意见,作者进行改正。

© 版权声明
THE END
喜欢就支持一下吧
点赞0 分享