React奇奇怪怪技巧之内联hook

 更新时间:2026年03月30日 08:38:36   作者:fe小陈  
Hooks是React提供的特殊函数,专门用于函数组件,让你无需编写类组件,就能使用 React 的核心特性(状态、副作用、上下文、缓存等),这篇文章主要介绍了React奇奇怪怪技巧之内联hook的相关资料,需要的朋友可以参考下

本文介绍一种 react 当中在一堆 jsx 中嵌入 hook 的方式 inlineHook

场景 or 为什么

一、列表渲染

列表渲染中,你或许需要为每个元素准备其状态和副作用,这时候你就需要用一个新的组件,如果需要用到父组件的状态,还需要透传一堆 props 给它。也就是说,你为了使用一个组件,需要包一个新的组件出来。

function Parent(){
    // states
    // effects 
    return <>
        {list.map(item =>
            <ChildComponentWrap
                key={item.key} 
               // ...state
               // ...callback
               // 这里其实可能很多
            />
        )}
    </>
}
// 一个黑盒组件
function ChildComponent(props){
    return <>...</>
}
// 为了维护它的状态而包装的组件
function ChildComponentWrap(props){
   // states
   // effects
   return <ChildComponent
               // ...state
               // ...callback
   />
}

作为一个很擅长堆屎山的人,尝试过下面这样的代码,但是由于 react hook 的固定规则,元素数量发生变化的时候,必然报错。(相信一些 react 新手肯定掉过这个坑)

function Parent(){
    // states
    // effects 
    return <>
        {list.map(item =>{
              useState()
              useEffect(()=>{}, [xxx]);
              return <ChildComponentWrap
                    key={item.key} 
                   // ...state
                   // ...callback
                   // 这里其实可能很多
                />
            }
        )}
    </>
}

所以需要某种方法,让它支持内联 hook,就有了 inlineHook 的这样一个工具,原理很简单,它创造了一个组件,并把你的代码块运行在里面,所以里面可以用 hook,当第一参数为 string 的时候,作为组件的 key。

function Parent(){
    // states
    // effects 
    return <>
        {list.map(item => 
            inlineHook(item.key, () => {
              useState()
              useEffect(()=>{}, [xxx]);
              return <ChildComponent
                   // ...state
                   // ...callback
                />
            })
        )}
    </>
}

二、条件副作用

错误代码

function AComponent(){
    // 注意这里,condition false true 转换的时候会报错
    if(condition){
        useEffect(()=>{}, [])
    }
    return <>...</>
}

使用 inlineHook,基于条件挂载组件的方式就可以实现条件 hook。

function AComponent(){
    return <>
        {condition && inlineHook(()=>{
            useEffect(()=>{}, []);
        })}
        ...
    </>
}

再考虑一个这样的 case:有个长链推送文本,当文本变化的时候,将文本变为绿色,持续 3s 之后恢复黑色。在你可以接受的情况下,何必为了小小一个 text 变色去创建一个组件呢?

const text = useReceiveMessage();
return <>
    <div>别的什么东西</div>
    {
        text && inlineHook(text, () => {
            const { value } = useCountdown(3);
            return <span style={{ color: value ? 'green' : '#000' }}>
                {text}
            </span>
        })
    }
    <div>别的什么东西</div>
<>

三、表单联动

想象这样一个场景,你有一个条件表单。

  1. A 会限制 B 的选项
    • A 选 'a' 时,B 可选 '1'、'2'、'3';
    • A 选 'b' 时,B 可选 '3'、'4'、'5';
    • A 选 'c' 时,B 可选 '5'、'6'、'7';
  2. 当 A 切换的时候,你需要清除已选中的 B选项(注意上面的 B可选项是有重叠的,有些值可以保留)
return <Form>
    <Form.Select
        field='A'
        optionList={['a','b','c']}
    />
    {* 可能离得很远 *}
    <Form.Select
        field='B'
        multiple  // 多选
        optionList={[...]} // 想办法映射一下选项
    />
</Form>

思考一下,怎么弄。

可以在 A 的 onChange 里面处理一下

<Form.Select
    field='A'
    optionList={['a','b','c']}
    onChange={(v) => { // 可以提出去,但是逻辑也很跳跃
        const BOptionList = getBOptionList(v);
        const newValues = ... 
        const values = formApi.getValue('B')
        if(!isEqual(values,newValues)){
           formApi.setValue('B', newBValue)
        }
    }}
/>
{* 可能离得很远,不一定会关注到这两个表单项的关系 *}
 <Form.Select
    field='B'
    multiple  // 多选
    optionList={[...]} // 想办法映射一下选项
/>

也可以拆组件,对吗?如我之前所说,你需要在组件间跳跃,上下文 props 需要传递。

function FieldB(props){
    const AValue = useFieldValue("A");
    const formApi = useFormApi();
    const BOptionList = getBOptionList(AValue);
    useEffect(() => {
        const values = formApi.getValue("B");
        // 检查一下 B 的值在不在 optionList 里面,不在就清除掉
        const newValues = ... // 想办法处理出来
        if(!isEqual(values, newValues)){
            form.setValue("B", newValues);
        }
    }, [AValue]);
    return <Form.Select
        field='B'
        optionList={BOptionList}
    />
}

使用 inlineHook 之后,大概就是这样。这并不是说这是什么好办法,这是一个偏好问题,但至少,不需要一个新的组件(当你需要频繁修改的时候,过多的组件跳转会影响编码效率),组件的使用与联动关系可以放得近。

return <Form>
    <Form.Select
        field='A'
        optionList={['a','b','c']}
    />
    {* 可能离得很远 *}
    {inlineHook(()=>{
        const AValue = useFieldValue("A");
        const formApi = useFormApi();
        useEffect(() => {
            const BOptionList = getBOptionList(v);
            const values = formApi.getValue("B");
            // 检查一下 B 的值在不在 optionList 里面,不在就清除掉
            const newValues = ... // 想办法处理出来
            if(!isEqual(values, newValues)){
                form.setValue("B", newValues);
            }
        }, [AValue]);
        return <Form.Select
            field='B'
            optionList={[...]} // 想办法映射一下选项
        />
    }}
</Form>

四、类组件里面不能使用 hook 吗

虽然这年头应该没有什么人会用类组件了,如下,这个用法也是写文章的时候才想起来的,我觉得是可行的。

class AComponent extends Component{
    state = {
        a: 1 
    }
    render(){
       return inlineHook(() => {
               const [b, setB] = useState(2);
               this.state.a;
               useEffect(() => {
               }, [this.state.a, b])
               return <>...</>
       })
    }
}

用法

return <>
    {* 直接嵌入 *}
    {inlineHook(()=>{
        useState();
        useEffect(()=>{}, []);
        return <>可以返回elem渲染</>
    })}
    {* 条件hook *}
    {
        condition && inlineHook(()=>{
            useEffect(()=>{},[])
            return <>可以返回elem渲染</>
        })
    }
    {* 列表 *}
    {
        list.map(item=>
            inlineHook(item.key, ()=>{
                useState();
                useEffect(()=>{}, []);
                return <>可以返回elem渲染</>;
            }))
    }
</>

好处

  1. 减少一次性 Wrapper 组件
  2. 在编码早期,频繁修改代码的时候,可以尽快验证,而不需要创建一个新的组件

坏处

  1. 你需要绕过这个文件的 react-hook eslint 检查,破坏了规范
  2. 代码会越写越长,人会越来越懒(用爽了都不想重构了)

源码

import React, { createElement, isValidElement } from 'react';
function Hook({ fn }: { fn: () => any }) {
  const res = fn();
  return isValidElement(res) ? res : null;
}
const NullComponent = () => null;
/**
 * 挂载 hook,当你不想为它写一个组件或者不知挂哪个组件上的时候
 * @param fn 传入一个 hook 函数
 * @returns
 */
export function inlineHook(fn: () => any): React.ReactElement;
export function inlineHook(key: string, fn: () => any): React.ReactElement;
export function inlineHook(...args: any[]) {
  const [a, b] = args;
  if (typeof a === 'function') {
    return createElement(Hook, {
      fn: a,
    });
  } else if (typeof a === 'string') {
    return createElement(Hook, {
      key: a,
      fn: b,
    });
  } else {
    return createElement(NullComponent, {});
  }
}

总结 

到此这篇关于React内联hook的文章就介绍到这了,更多相关React内联hook内容请搜索脚本之家以前的文章或继续浏览下面的相关文章希望大家以后多多支持脚本之家!

相关文章

  • React 中的 ForwardRef的使用示例详解

    React 中的 ForwardRef的使用示例详解

    forwardRef 相当于是为 ref 传递的一种方式,普通的函数式组件就是 Render,而 fowardRef 多加了 Ref 参数,这篇文章主要介绍了React 中的 ForwardRef的使用示例详解,需要的朋友可以参考下
    2024-06-06
  • 解决React报错Cannot find namespace context

    解决React报错Cannot find namespace context

    这篇文章主要为大家介绍了React报错Cannot find namespace context分析解决,有需要的朋友可以借鉴参考下,希望能够有所帮助,祝大家多多进步,早日升职加薪
    2022-12-12
  • React中useLayoutEffect钩子使用场景详解

    React中useLayoutEffect钩子使用场景详解

    这篇文章主要为大家介绍了React中useLayoutEffect钩子使用场景详解,有需要的朋友可以借鉴参考下,希望能够有所帮助,祝大家多多进步,早日升职加薪
    2022-12-12
  • React Router 中实现嵌套路由和动态路由的示例

    React Router 中实现嵌套路由和动态路由的示例

    React Router 是一个非常强大和灵活的路由库,它为 React 应用程序提供了丰富的导航和 URL 管理功能,能够帮助我们构建复杂的单页应用和多页应用,这篇文章主要介绍了React Router 中如何实现嵌套路由和动态路由,需要的朋友可以参考下
    2023-05-05
  • react优雅处理多条件鼠标拖拽位移

    react优雅处理多条件鼠标拖拽位移

    这篇文章主要为大家详细介绍了react优雅处理多条件鼠标拖拽位移,文中示例代码介绍的非常详细,具有一定的参考价值,感兴趣的小伙伴们可以参考一下
    2022-08-08
  • React中上传图片到七牛的示例代码

    React中上传图片到七牛的示例代码

    本篇文章主要介绍了React中上传图片到七牛的示例代码,小编觉得挺不错的,现在分享给大家,也给大家做个参考。一起跟随小编过来看看吧
    2017-10-10
  • React生命周期方法之componentDidMount的使用

    React生命周期方法之componentDidMount的使用

    这篇文章主要介绍了React生命周期方法之componentDidMount的使用,具有很好的参考价值,希望对大家有所帮助。如有错误或未考虑完全的地方,望不吝赐教
    2023-06-06
  • 聊聊ant design charts 获取后端接口数据展示问题

    聊聊ant design charts 获取后端接口数据展示问题

    今天在做项目的时候遇到几个让我很头疼的问题,一个是通过后端接口成功访问并又返回数据,但拿不到数据值。其二是直接修改state中的data,console中数组发生变化但任然数据未显示,这篇文章主要介绍了ant design charts 获取后端接口数据展示,需要的朋友可以参考下
    2022-05-05
  • React手写一个手风琴组件示例

    React手写一个手风琴组件示例

    这篇文章主要为大家介绍了React手写一个手风琴组件示例详解,有需要的朋友可以借鉴参考下,希望能够有所帮助,祝大家多多进步,早日升职加薪
    2022-07-07
  • React组件之多选Checkbox实例

    React组件之多选Checkbox实例

    这篇文章主要介绍了React组件之多选Checkbox实例,具有很好的参考价值,希望对大家有所帮助,
    2023-10-10

最新评论