如何通过 react-redux 使用 refs 进行反应,withRouter?

Posted

技术标签:

【中文标题】如何通过 react-redux 使用 refs 进行反应,withRouter?【英文标题】:How to use refs in react through react-redux , withRouter? 【发布时间】:2019-12-10 07:17:48 【问题描述】:

我正在尝试在连接到 react-redux 的组件中使用 ref

我已经尝试过这个解决方案。

connect(null, null, null, forwardRef: true)(myCom)

<myCom ref=ref => this.myCom = ref />

根据 react-redux 文档,这工作得很好,但是现在当我尝试使用时

withRouter 同时我得到一个错误:

函数组件不能被赋予 refs。尝试访问此 ref 将失败。你的意思是使用 React.forwardRef() 吗?

所以我尝试过导致上述错误的最终导出代码

export default connect(null, null, null,  forwardRef: true )(withRouter(withStyles(styles)(myCom)));

注意:withStyles 不会导致任何问题,因为我尝试仅删除 withRouter,问题已解决。

有什么办法可以解决这个问题吗?

【问题讨论】:

你的 myCom 组件是什么样的? @WillJenkins 这不是组件问题,我的组件是基于类的组件,我刚刚对其进行了测试,现在删除了withRouterwithStyles,只留下了我给它的第一个示例运行良好。但是当我使用 withRouter 和 withStyles 时出现错误。这里的问题是引用它自己的组件,例如如果您尝试使用 ref 而不使用 forwardRef: true 您需要引用它包含在 connect 中的组件,然后是 withRouter 然后是 withStyles,我认为这是问题 可能相关:github.com/mui-org/material-ui/issues/10106 @WillJenkins 我已经更新了问题只依赖于 withRouter 的问题 您尝试过使用 compose 吗? ***.com/questions/52823456/… 【参考方案1】:

你可以这样做!这肯定会起作用?

import  withRouter  from 'react-router';

//Just copy and add this withRouterAndRef HOC

const withRouterAndRef = (WrappedComponent) => 
class InnerComponentWithRef extends React.Component     
      render() 
          const  forwardRef, ...rest  = this.props;
          return <WrappedComponent ...rest ref=forwardRef />;
      
  
  const ComponentWithRouter = withRouter(InnerComponentWithRef,  withRef: true );
  return React.forwardRef((props, ref) => 
      return <ComponentWithRouter ...props forwardRef=ref />;
    );


class MyComponent extends Component 
   constructor(props) 
      super(props);
   


//export using withRouterAndRef

export default withRouterAndRef (MyComponent)

【讨论】:

【参考方案2】:

使用compose 方法并尝试类似的方法

const enhance = compose(
  withStyles(styles),
  withRouter,
  connect(null, null, null,  forwardRef: true )
)

并在导出组件之前使用它

export default enhance(MyComponent)

【讨论】:

【参考方案3】:

为了将引用传递给由withRouter 包裹的组件,您需要将其命名为wrappedComponentRef。我建议将 withRouter 作为最外层的包装器,因此您的示例如下所示:

withRouter(connect(null, null, null, forwardRef: true)(myCom));
<myCom wrappedComponentRef=ref => this.myCom = ref />

以下示例和描述改编自我的相关答案:Get ref from connected redux component withStyles

下面是react-redux todo list tutorial 的修改版本的代码,它显示了正确的语法。我在此处包含了我更改的两个文件(TodoList.js 和 TodoApp.js),但沙盒是一个完整的示例。

TodoApp 中,我使用TodoList 上的引用(通过wrappedComponentRef 属性)来获取并显示其高度。仅当TodoApp 重新渲染时,显示的高度才会更新,因此我包含了一个按钮来触发重新渲染。如果你在 todo 列表中添加了几个 todo,然后点击重新渲染按钮,你会看到列表的新高度显示出来(表明 ref 已经完全工作了)。

TodoList 中,我使用withStyles 在待办事项列表周围添加蓝色边框以显示withStyles 正在工作,并且我正在显示主题中的原色以显示withTheme工作中。我还展示了来自withRouterlocation 对象,以证明withRouter 正在工作。

TodoList.js

import React from "react";
import  connect  from "react-redux";
import Todo from "./Todo";
import  getTodosByVisibilityFilter  from "../redux/selectors";
import  withStyles, withTheme  from "@material-ui/core/styles";
import clsx from "clsx";
import  withRouter  from "react-router-dom";

const styles = 
  list: 
    border: "1px solid blue"
  
;

const TodoList = React.forwardRef(
  ( todos, theme, classes, location , ref) => (
    <>
      <div>Location (from withRouter): JSON.stringify(location)</div>
      <div>theme.palette.primary.main: theme.palette.primary.main</div>
      <ul ref=ref className=clsx("todo-list", classes.list)>
        todos && todos.length
          ? todos.map((todo, index) => 
              return <Todo key=`todo-$todo.id` todo=todo />;
            )
          : "No todos, yay!"
      </ul>
    </>
  )
);

const mapStateToProps = state => 
  const  visibilityFilter  = state;
  const todos = getTodosByVisibilityFilter(state, visibilityFilter);
  return  todos ;
;
export default withRouter(
  connect(
    mapStateToProps,
    null,
    null,
     forwardRef: true 
  )(withTheme(withStyles(styles)(TodoList)))
);

TodoApp.js

import React from "react";
import AddTodo from "./components/AddTodo";
import TodoList from "./components/TodoList";
import VisibilityFilters from "./components/VisibilityFilters";
import "./styles.css";

export default function TodoApp() 
  const [renderIndex, incrementRenderIndex] = React.useReducer(
    prevRenderIndex => prevRenderIndex + 1,
    0
  );
  const todoListRef = React.useRef();
  const heightDisplayRef = React.useRef();
  React.useEffect(() => 
    if (todoListRef.current && heightDisplayRef.current) 
      heightDisplayRef.current.innerhtml = ` (height: $
        todoListRef.current.offsetHeight
      )`;
    
  );
  return (
    <div className="todo-app">
      <h1>
        Todo List
        <span ref=heightDisplayRef />
      </h1>
      <AddTodo />
      <TodoList wrappedComponentRef=todoListRef />
      <VisibilityFilters />
      <button onClick=incrementRenderIndex>
        Trigger re-render of TodoApp
      </button>
      <div>Render Index: renderIndex</div>
    </div>
  );


【讨论】:

如果你的组件都不是类而不是函数怎么办。有大量的函数示例。 @mjwrazor 我建议您创建一个单独的问题,以便更全面地布局您的问题的细节。

以上是关于如何通过 react-redux 使用 refs 进行反应,withRouter?的主要内容,如果未能解决你的问题,请参考以下文章

react 中的 redux 和react-redux的区别分析

如何使用 react-redux 钩子测试组件?

使用通过 react、redux 和 react-redux 完成的组件以及在 react 应用程序中使用 webpack 构建时出错

你如何混合 componentDidMount() 和 react-redux connect()?

如何使用 thunk 和 useDispatch(react-redux 挂钩)从操作中返回承诺?

如何使用 react-redux 实现登录认证?