如何通过react-redux和路由器在ref中使用refs?

时间:2019-08-01 16:25:24

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

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

我已经尝试过此解决方案。

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

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

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

withRouter同时出现错误:

  

不能为功能组件提供参考。尝试访问此引用将失败。您是要使用React.forwardRef()吗?

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

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

注意:withStyles不会引起任何问题,因为我尝试仅使用withRouter删除,此问题已解决。

是否有解决此问题的解决方案?

3 个答案:

答案 0 :(得分:2)

为了将ref传递给由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中,我使用wrappedComponentRef上的ref(通过TodoList属性)获取并显示其高度。仅当TodoApp重新渲染时,显示的高度才会更新,因此我添加了一个按钮来触发重新渲染。如果将几个待办事项添加到待办事项列表中,然后单击“重新渲染”按钮,您将看到显示了列表的新高度(表明参考已完全正常工作)。

TodoList中,我正在使用withStyles在待办事项列表周围添加蓝色边框以显示withStyles在工作,并且正在显示主题的原色以表明withTheme在工作。我还将显示location中的withRouter对象,以证明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>
  );
}

Edit Todo App with Redux

答案 1 :(得分:1)

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

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

并在导出组件之前使用它

export default enhance(MyComponent)

答案 2 :(得分: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)