我正在使用Material-UI设计要构建的React应用。我试图在<Menu>
组件中使用自己的自定义功能组件,但遇到一个奇怪的错误:
Warning: Function components cannot be given refs.
Attempts to access this ref will fail.
Did you mean to use React.forwardRef()?
Check the render method of ForwardRef(Menu).
in TextDivider (at Filter.js:32)
发生这种情况的JSX看起来像这样:(第32行是TextDivider组件)
<Menu>
<TextDivider text="Filter Categories" />
<MenuItem>...</MenuItem>
</Menu>
TextDivider在哪里:
const TextDivider = ({ text }) => {
const [width, setWidth] = useState(null);
const style = {
display: 'flex',
justifyContent: 'space-between',
alignItems: 'center'
};
const hrStyle = {
width: `calc(100% - ${width}px)`,
marginRight: 0,
marginLeft: 0
};
const spanStyle = {
color: '#555',
whiteSpace: 'nowrap'
};
const ref = createRef();
useEffect(() => {
if (ref.current) {
const width = ref.current.getBoundingClientRect().width + 35;
console.log('width', width);
setWidth(width);
}
}, [ref.current]);
return (
<div style={style}>
<span ref={ref} style={spanStyle}>{ text }</span>
<Divider className="divider-w-text" style={ hrStyle }/>
</div>
);
};
奇怪的是,当在常规容器中自行设置此组件时,该组件呈现完美的效果,但似乎仅在<Menu>
组件内部呈现时才会引发此错误。
此外,我发现如果将TextDivider像这样包装在div中,该错误会完全消失,这真的很奇怪:
<Menu>
<div>
<TextDivider text="Filter Categories" />
</div>
<MenuItem>...</MenuItem>
</Menu>
但是,这对我来说就像是胶带,我宁愿理解潜在的问题,即为什么我不能只在菜单中渲染此组件。我没有在我的功能组件中使用任何类型的Material-UI引用,也没有在我的<Divider>
组件中的<TextDivider>
组件上放置引用,所以我不明白为什么会抛出此错误错误。
任何对这种行为发生原因的见解将不胜感激。
我尝试使用useCallback
钩子,createRef()
,useRef()
,并阅读了在功能组件中使用钩子时可以找到的所有内容。该错误本身似乎具有误导性,因为即使React文档本身也使用此处的功能组件引用显示:https://reactjs.org/docs/hooks-reference.html#useref
答案 0 :(得分:1)
Menu
使用Menu的第一个子项作为Menu内部使用的Popover组件的“内容锚”。 “内容锚点”是Popover尝试与锚点元素(菜单外部的元素,它是放置菜单的参考点)对齐的菜单中的DOM元素。
为了利用第一个孩子作为内容锚点,Menu向其添加了一个引用(使用cloneElement)。为了避免收到您收到的错误(并使定位正常工作),功能组件需要将ref转发到它提供的组件之一(通常是最外面的组件-在您的情况下为div)。
当您将div
用作Menu
的直接子代时,您不会收到错误消息,因为div可以成功接收到引用。
所引用的SakoBu文档包含有关如何将引用转发到功能组件的详细信息。
结果应类似于以下内容(但我正在通过电话回答此问题,如果出现语法错误,请您谅解):
const TextDivider = React.forwardRef(({ text }, ref) => {
const [width, setWidth] = useState(null);
const style = {
display: 'flex',
justifyContent: 'space-between',
alignItems: 'center'
};
const hrStyle = {
width: `calc(100% - ${width}px)`,
marginRight: 0,
marginLeft: 0
};
const spanStyle = {
color: '#555',
whiteSpace: 'nowrap'
};
// Separate bug here.
// This should be useRef instead of createRef.
// I’ve also renamed this ref for clarity, and used "ref" for the forwarded ref.
const spanRef = React.useRef();
useEffect(() => {
if (spanRef.current) {
const width = spanRef.current.getBoundingClientRect().width + 35;
console.log('width', width);
setWidth(width);
}
}, [spanRef.current]);
return (
<div ref={ref} style={style}>
<span ref={spanRef} style={spanStyle}>{ text }</span>
<Divider className="divider-w-text" style={ hrStyle }/>
</div>
);
});
您可能还会发现以下答案很有用: