【问题标题】:How to use refs in react through react-redux , withRouter?如何通过 react-redux 使用 refs 进行反应,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,我认为这是问题
  • @WillJenkins 我已经更新了问题只依赖于 withRouter 的问题
  • 您尝试过使用 compose 吗? stackoverflow.com/questions/52823456/…

标签: reactjs react-redux material-ui react-router-dom


【解决方案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 我建议您创建一个单独的问题,以便更全面地布局您的问题的细节。
      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2018-12-04
      • 2017-07-16
      • 2015-10-25
      • 1970-01-01
      • 2019-02-05
      • 1970-01-01
      相关资源
      最近更新 更多