React 使用 Typescript 动态创建 refs
Posted
技术标签:
【中文标题】React 使用 Typescript 动态创建 refs【英文标题】:React creating dynamically refs with Typescript 【发布时间】:2020-01-14 00:53:25 【问题描述】:我有一个table
列表,其中每一行都有一个menu
按钮,为此我需要一个ref
。我在我的项目中使用了 react mui,它是menu。我尝试过像这样创建参考:
const rows = props;
const refs = Array.from(length: rows.length, a => React.useRef<htmlButtonElement>(null));
然后尝试在每个button
上像这样使用map
里面的函数:
<Button
ref=refs[index]
aria-controls="menu-list-grow"
aria-haspopup="true"
onClick=() => handleToggle(row.id)
>Velg
</Button>
<Popper open=!!checkIfOpen(row.id) anchorEl=refs[index].current keepMounted transition disablePortal>
(TransitionProps, placement) => (
<Grow
...TransitionProps
style=transformOrigin: placement === 'bottom' ? 'center top' : 'center bottom'>
<Paper id="menu-list-grow">
<ClickAwayListener onClickAway=(e) => handleClose(e, refs[index].current)>
<MenuList>
<MenuItem
onClick=(e) => handleClose(e, refs[index].current)>Profile</MenuItem>
<MenuItem onClick=(e) => handleClose(e, refs[index].current)>My account</MenuItem>
<MenuItem onClick=(e) => handleClose(e, refs[index].current)>Logout</MenuItem>
</MenuList>
</ClickAwayListener>
</Paper>
</Grow>
)
</Popper>
但是,然后我得到一个错误:
无法在回调中调用 React Hook "React.useRef"。反应 Hooks 必须在 React 函数组件或自定义 React 中调用 钩子函数 react-hooks/rules-of-hooks
我怎样才能动态地做到这一点,以便我可以在 map 函数中使用 refs。我已经尝试过答案中的建议,但我无法让它发挥作用。 这是codesandbox of the example。
【问题讨论】:
How target DOM with react useRef in map的可能重复 【参考方案1】:useRef
与 React.createRef 并不完全相同。
最好叫它useInstanceField
:)
所以,您的代码可能有点不同。
第一步:我们使用useRef
来保存refs数组:
const rows = props;
const refs = useRef(Array.from(length: rows.length, a => React.createRef()));
然后,在您的 map 函数中,我们将每个 ref 保存到 refs 数组中的索引:
<Button
ref=refs.current[index]
aria-controls="menu-list-grow"
aria-haspopup="true"
onClick=() => handleToggle(row.id)
>Velg
</Button>
<Popper open=!!checkIfOpen(row.id) anchorEl=refs.current[index].current keepMounted transition disablePortal>
(TransitionProps, placement) => (
<Grow
...TransitionProps
style=transformOrigin: placement === 'bottom' ? 'center top' : 'center bottom'>
<Paper id="menu-list-grow">
<ClickAwayListener onClickAway=(e) => handleClose(e, refs.current[index].current)>
<MenuList>
<MenuItem
onClick=(e) => handleClose(e, refs.current[index].current)>Profile</MenuItem>
<MenuItem onClick=(e) => handleClose(e, refs.current[index].current)>My account</MenuItem>
<MenuItem onClick=(e) => handleClose(e, refs.current[index].current)>Logout</MenuItem>
</MenuList>
</ClickAwayListener>
</Paper>
</Grow>
)
</Popper>
如果你的长度改变了,你应该在useEffect
处理它来改变refs的长度
您也可以使用其他方式:
1) 创建一个引用数组,但没有 React.createRef:
const rows = props;
const refs = useRef(new Array(rows.length));
在map中我们使用ref=el => refs.current[index] = el
来存储ref
<Button
ref=el => refs.current[index] = el
aria-controls="menu-list-grow"
aria-haspopup="true"
onClick=() => handleToggle(row.id)
>Velg
</Button>
<Popper open=!!checkIfOpen(row.id) anchorEl=refs.current[index].current keepMounted transition disablePortal>
(TransitionProps, placement) => (
<Grow
...TransitionProps
style=transformOrigin: placement === 'bottom' ? 'center top' : 'center bottom'>
<Paper id="menu-list-grow">
<ClickAwayListener onClickAway=(e) => handleClose(e, refs.current[index])>
<MenuList>
<MenuItem
onClick=(e) => handleClose(e, refs.current[index])>Profile</MenuItem>
<MenuItem onClick=(e) => handleClose(e, refs.current[index])>My account</MenuItem>
<MenuItem onClick=(e) => handleClose(e, refs.current[index])>Logout</MenuItem>
</MenuList>
</ClickAwayListener>
</Paper>
</Grow>
)
</Popper>
【讨论】:
我无法编译这个,我得到 Typescript 错误:Expected 0 arguments, but got 1.
对于这一行:React.createRef(null)
对不起,是的,只需删除空参数:) 我已经更新了答案
我也这样做了,但后来我得到:Type 'RefObject<>' is not assignable to type '((instance: HTMLButtonElement | null) => void) | RefObject<HTMLButtonElement> | null | undefined'. Type 'RefObject<>' is not assignable to type 'RefObject<HTMLButtonElement>'.
看来打字稿不允许使用这种方式。所以我用 TypeScript codesandbox.io/s/wizardly-goldstine-33nw5 做了一个例子
我尝试将您的解决方案实施到我的示例中,但随后出现错误:Type 'HTMLButtonElement | null' is not assignable to type 'never'. Type 'null' is not assignable to type 'never'.
如果我将参考更改为:const refs = React.useRef<Array<React.RefObject<HTMLButtonElement> | null>>([]);
,则会出现错误:Type 'HTMLButtonElement | null' is not assignable to type 'RefObject<HTMLButtonElement> | null'. Property 'current' is missing in type 'HTMLButtonElement' but required in type 'RefObject<HTMLButtonElement>'
跨度>
【参考方案2】:
这里是另一种选择:
const textInputRefs = useRef<(HTMLDivElement | null)[]>([])
...
const onClickFocus = (event: React.BaseSyntheticEvent, index: number) =>
textInputRefs.current[index]?.focus()
;
...
items.map((item, index) => (
<textInput
inputRef=(ref) => textInputs.current[index] = ref
/>
<Button
onClick=event => onClickFocus(event, index)
/>
【讨论】:
以上是关于React 使用 Typescript 动态创建 refs的主要内容,如果未能解决你的问题,请参考以下文章
深入浅出TypeScript- 在React项目中使用TypeScript
使用 wmonk vs Microsoft 的 Typescript 创建 React App (CRA)?