react forwardRef和readux的connect冲突,导致ref.current获取不到值

问题描述:

forwardRef和readux的connect冲突,导致ref.current获取不到值

我想封装一个组件,父组件调用子组件的方法。但因为组件里使用了redux的connect导致ref一直获取不到值。

javascript 复制代码
import React, { useState, useEffect, ReactNode, forwardRef, useImperativeHandle } from 'react';
import { Modal} from 'antd';

import { connect } from 'react-redux'
import * as actions from '@/store/actions'
import "./index.less";


interface Props {
    /** 标题内容 */
    title?: string | ReactNode;
    /** 操作类型 */
    type: "add" | "edit";
    /** 数据 */
    fundData?: any;
    storeData?: any;
    setStoreData?: any;
    children?: ReactNode;

}
/** 
 * 策略弹窗表单 
 * 本组件可传一children点击传入的children来控制弹窗打开
 * 还可以用ref 来控制弹窗打开和关闭
 * 父组件引用useRef const modalRef:any = useRef(); modalRef.current?.openModal();
 * 然后<ClModalForm type="edit" fundData={{}} ref={modalRef}/>
 * 
*/

const ClModalForm = forwardRef((props: Props, ref) => {
    const { title, children } = props;
    //编辑策略 Modal是否展示
    const [cl, setCl] = useState(false);

    // 暴露 openModal、handleCancel 方法给父组件
    useImperativeHandle(ref, () => ({
        openModal,
        handleCancel,
    }));
    const handleCancel = () => {
        setCl(false);
    };

    /** 打开弹窗 */
    const openModal = () => {
        setCl(true);

    };

    return (
        <div>
            {children && <div onClick={openModal}>{children}</div>}
            <Modal title={title || "编辑策略"}
                visible={cl}
                onCancel={handleCancel}
                width={560}
                destroyOnClose
                bodyStyle={{
                    maxHeight: "70vh",  // 控制最大高度
                    overflowY: "auto",  // 垂直滚动
                }}
                centered
            >

            </Modal>
        </div>
    )
});
export default connect((state) => state, actions)(ClModalForm);

使用:

javascript 复制代码
const modalRef: any = useRef();
<ClModalForm title={"补充策略信息"} type="edit" fundData={currentRow} ref={modalRef} />

然后
if(modalRef.current){
   modalRef.current?.openModal();
}

问题原因:

1.Redux connect 会阻断 ref 转发,默认不会向下传递 ref。

2.直接使用 connect(...)(forwardRef(...)) 时,ref 会被高阶组件拦截。

解决方案:

方案 适用场景 优点 缺点
1. forwardRef配置 React Redux 6+ 最简洁官方方案 需要较新版本
2. 双forwardRef 所有版本 通用性强 代码稍复杂
3. 自定义ref属性 简单场景 无需高阶组件 非标准ref用法
4. withRef Redux 5.x 旧版兼容方案 已弃用

方案一:最佳实践 - 使用 React Redux 的 forwardRef 配置(推荐):

导出把ref透传打开:

javascript 复制代码
export default connect((state) => state, actions, null,
    { forwardRef: true } // 显式启用 ref 不加父组件拿不到子组件的ref
)(ClModalForm);

或者:
// 解决方案:添加 { forwardRef: true } 配置
const ConnectedClModalForm = connect(
  (state) => state,
  actions,
  null,
  { forwardRef: true }  // 关键配置
)(ClModalForm);

export default ConnectedClModalForm;

redux版本<6请看方案四!
版本兼容性注意事项:
v6.0 以下:必须用 { withRef: true },并通过 getWrappedInstance() 间接访问 ref1。
v6.0 及以上:优先使用 { forwardRef: true },直接获得 ref 引用。

方案二:双 forwardRef 包裹法:

两层转发:

javascript 复制代码
const InnerClModalForm = forwardRef<ModalHandles, Props>((props, ref) => {
  // ...组件内部逻辑不变
});

const Connected = connect((state) => state, actions)(InnerClModalForm);

// 外层 forwardRef 处理
const ClModalForm = forwardRef<ModalHandles, Props>((props, ref) => (
  <Connected {...props} forwardedRef={ref} />
));

export default ClModalForm;

方案三:自定义 ref 属性传递:

javascript 复制代码
interface Props {
  innerRef?: React.Ref<ModalHandles>;
  // ...其他props
}

const ClModalForm = (props: Props) => {
  // 使用 props.innerRef 替代 ref
  useImperativeHandle(props.innerRef, () => ({
    openModal,
    handleCancel
  }));
  
  // ...组件内部逻辑不变
};

export default connect((state) => state, actions)(ClModalForm);

// 父组件使用方式
function Parent() {
  const ref = useRef<ModalHandles>(null);
  
  return <ClModalForm innerRef={ref} />;
}

方案四:使用 withRef 选项(旧版 React Redux):

javascript 复制代码
// 适用于 React Redux 5.x 及以下版本
const ConnectedClModalForm = connect(
  (state) => state,
  actions,
  null,
  { withRef: true }  // 旧版配置
)(forwardRef(ClModalForm));

export default ConnectedClModalForm;
相关推荐
_山海1 分钟前
Bun入门指南
前端·javascript·后端
想睡懒觉7 分钟前
我用 CSS 3D 做了个星空隧道 Loading,没有 Three.js
前端
labixiong11 分钟前
transition 写在 CSS 变量上为什么不生效?给变量补张「类型身份证」
前端·css·html
YHL19 分钟前
🧠 Agent 记忆进阶:总结压缩与向量检索 —— 从截断到 Milvus 长期记忆
前端·人工智能
YHL20 分钟前
🎯 JavaScript 单例模式(Singleton Pattern)—— 从理论到实战
javascript·设计模式
用户9210802628629 分钟前
前端 Vue 专栏 05:从 Object.defineProperty 到 Proxy,彻底理解 Vue 响应式原理
前端
Fluxart.ai39 分钟前
GPT Image 2.5 有使用次数限制吗?额度、并发、频率限制说明
服务器·前端·gpt
程序员海军1 小时前
AI 越来越强,为什么打工人反而越来越累、越来越内耗了?
前端·程序员·aigc
vx_Biye_Design1 小时前
springboot游泳馆系统93765-计算机课程设计、毕业设计
java·javascript·spring boot·后端·python·spring·课程设计
晴天161 小时前
VSCodium 项目介绍
前端·javascript·vscode