在使用反应路由器时,如何避免“功能组件不能给予参考”?

我有以下(使用材质用户界面) ... 。

import React from "react";
import { NavLink } from "react-router-dom";
import Tabs from "@material-ui/core/Tabs";
import Tab from "@material-ui/core/Tab";
function LinkTab(link){
return <Tab component={NavLink}
to={link.link}
label={link.label}
value={link.link}
key={link.link}
/>;
}

在新版本中,这会导致以下警告..。

警告: 不能给函数组件提供参考文献 这个引用将会失败。您的意思是使用 React.forwardRef ()吗?

检查 ForwardRef的渲染方法。 在 NavLink 中(由 ForwardRef 创建)

我试着换衣服。

function LinkTab(link){
// See https://material-ui.com/guides/composition/#caveat-with-refs
const MyLink = React.forwardRef((props, ref) => <NavLink {...props} ref={ref} />);
return <Tab component={MyLink}
to={link.link}
label={link.label}
value={link.link}
key={link.link}
/>;
}

但我仍然得到警告。我如何解决这个问题?

258492 次浏览

来自 react-routerNavLink是一个函数组件,它是 林克的一个特殊版本,为此暴露了一个 innerRef道具。

// required for react-router-dom < 6.0.0
// see https://github.com/ReactTraining/react-router/issues/6056#issuecomment-435524678
const MyLink = React.forwardRef((props, ref) => <NavLink innerRef={ref} {...props} />);

你也可以搜索我们的文件为 react-router这导致你到 https://mui.com/getting-started/faq/#how-do-i-use-react-router链接到 https://mui.com/components/buttons/#third-party-routing-library。最后一个链接提供了一个工作示例,并解释了这种情况在响应路由器 v6中可能发生的变化

您可以使用 裁判而不是 裁判。这只是因为它避免了特殊的道具名称 裁判

<InputText
label="Phone Number"
name="phoneNumber"
refs={register({ required: true })}
error={errors.phoneNumber ? true : false}
icon={MailIcon}
/>

把它设成 innerRef,

// Client.js
<Input innerRef={inputRef} />

用它作为 ref

// Input.js
const Input = ({ innerRef }) => {
return (
<div>
<input ref={innerRef} />
</div>
)
}
const renderItem = ({ item, index }) => {


return (
<>
<Item
key={item.Id}
item={item}
index={index}
/>
</>
);
};

使用片段解决 React.forwardRef () ? 警告

如果您发现无法将自定义 ref道具或 forwardRef添加到组件中,我还有一个技巧可以为您的功能组件获取 ref 对象。

假设您想将 ref 添加到自定义函数组件,比如:

 const ref = useRef();


//throws error as Button is a functional component without ref prop
return <Button ref={ref}>Hi</Button>;

您可以将它包装在一个通用的 html 元素中,并在该元素上设置 ref

 const ref = useRef();


// This ref works. To get button html element inside div, you can do
const buttonRef = ref.current && ref.current.children[0];
return (
<div ref={ref}>
<Button>Hi</Button>
</div>
);

当然要相应地管理状态以及在哪里使用 buttonRef 对象。

为了修复这个警告,你应该用 forwardRef函数很好地包装你的定制组件,就像在这个 博客中提到的那样

    const AppTextField =(props) {return(/*your component*/)}

将下列代码更改为

const AppTextField = forwardRef((props,ref) {return(/*your component*/)}

在我们的例子中,我们将一个 SVG 组件(Site 的 Logo)直接传递给 NextJS 的 链接组件,这是一个有点自定义的组件,我们得到了这样的错误。

使用了 SVG 并“导致”问题的头部组件。

import Logo from '_public/logos/logo.svg'
import Link from '_components/link/Link'


const Header = () => (
<div className={s.headerLogo}>
<Link href={'/'}>
<Logo />
</Link>
</div>
)

控制台上的错误消息

Function components cannot be given refs. Attempts to access this ref will fail.
Did you mean to use React.forwardRef()?

自定义链接组件

import NextLink from 'next/link'
import { forwardRef } from 'react'


const Link = ({ href, shallow, replace, children, passHref, className }, ref) => {
return href ? (
<NextLink
href={href}
passHref={passHref}
scroll={false}
shallow={shallow}
replace={replace}
prefetch={false}
className={className}
>
{children}
</NextLink>
) : (
<div className={className}>{children}</div>
)
}


export default forwardRef(Link)


现在我们确保在定制的链接组件中使用了 forwardRef,但是我们仍然得到了这个错误。

为了解决这个问题,我将 SVG 元素的包装器定位改为 this 和 翻译: pestwave 校对: pestwave 校对: pestwave 校对: pestwave 校对: pestwave 校对: pestwave 校对: pestwave 校对: pestwave 校对: pestwave 校对: pestwave 校对:

const Header = () => (
<Link href={'/'}>
<div className={s.headerLogo}>
<Logo />
</div>
</Link>
)

我只是在这里粘贴 skychavda解决方案,因为它提供了一个子引用: 所以您可以直接从父级调用子方法或子引用,而不需要任何警告。

来源: https://github.com/reactjs/reactjs.org/issues/2120

/* Child.jsx */
import React from 'react'


class Child extends React.Component {
componentDidMount() {
const { childRef } = this.props;
childRef(this);
}
componentWillUnmount() {
const { childRef } = this.props;
childRef(undefined);
}
alertMessage() {
window.alert('called from parent component');
}
render() {
return <h1>Hello World!</h1>
}
}


export default Child;
/* Parent.jsx */
import React from 'react';
import Child from './Child';


class Parent extends React.Component {
onClick = () => {
this.child.alertMessage(); // do stuff
}
render() {
return (
<div>
<Child childRef={ref => (this.child = ref)} />
<button onClick={this.onClick}>Child.alertMessage()</button>
</div>
);
}
}

如果您使用的是功能组件,那么 React.forwardRef 是一个非常好的特性,可以帮助您了解如何在这样的场景中使用它。如果最终读到这篇文章的人手更多的话,我就给你们准备了一个代码盒子,你们可以随便玩玩。有时候它最初不加载 Styled-Component,所以当沙箱加载时,您可能需要刷新内联浏览器。

Https://codesandbox.io/s/react-forwardref-example-15ql9t?file=/src/app.tsx

// MyAwesomeInput.tsx
import React from "react";
import { TextInput, TextInputProps } from "react-native";
import styled from "styled-components/native";


const Wrapper = styled.View`
width: 100%;
padding-bottom: 10px;
`;


const InputStyled = styled.TextInput`
width: 100%;
height: 50px;
border: 1px solid grey;
text-indent: 5px;
`;


// Created an interface to extend the TextInputProps, allowing access to all of its properties
// from the object that is created from Styled-Components.
//
// I also define the type that the forwarded ref will be.
interface AwesomeInputProps extends TextInputProps {
someProp?: boolean;
ref?: React.Ref<TextInput>;
}


// Created the functional component with the prop type created above.
//
// Notice the end of the line, where you wrap everything in the React.forwardRef().
// This makes it take one more parameter, called ref. I showed what it looks like
// if you are a fan of destructuring.
const MyAwesomeInput: React.FC<AwesomeInputProps> = React.forwardRef( // <-- This wraps the entire component, starting here.
({ someProp, ...props }, ref) => {
return (
<Wrapper>
<InputStyled {...props} ref={ref} />
</Wrapper>
);
}); // <-- And ending down here.


export default MyAwesomeInput;

然后在调用屏幕上,创建 ref 变量并将其传递到组件的 ref 字段。

// App.tsx
import React from "react";
import { StyleSheet, Text, TextInput, View } from "react-native";
import MyAwesomeInput from "./Components/MyAwesomeInput";


const App: React.FC = () => {
// Set some state fields for the inputs.
const [field1, setField1] = React.useState("");
const [field2, setField2] = React.useState("");


// Created the ref variable that we'll use down below.
const field2Ref = React.useRef<TextInput>(null);


return (
<View style={styles.app}>
<Text>React.forwardRef Example</Text>
<View>
<MyAwesomeInput
value={field1}
onChangeText={setField1}
placeholder="field 1"
// When you're done typing in this field, and you hit enter or click next on a phone,
// this makes it focus the Ref field.
onSubmitEditing={() => {
field2Ref.current.focus();
}}
/>
<MyAwesomeInput
// Pass the ref variable that's created above to the MyAwesomeInput field of choice.
// Everything should work if you have it setup right.
ref={field2Ref}
value={field2}
onChangeText={setField2}
placeholder="field 2"
/>
</View>
</View>
);
};


const styles = StyleSheet.create({
app: {
flex: 1,
justifyContent: "center",
alignItems: "center"
}
});


export default App;

就是这么简单! 无论您将 MyAwesomeInput 组件放在哪里,都可以使用 ref。