使用Typescript反应创建动态引用

时间:2019-09-12 07:27:37

标签: javascript reactjs react-ref

我有一个table列表,其中每一行都有一个menu按钮,为此我需要一个ref。我在我的项目中使用react mui,它是menu。我试过创建这样的裁判:

const {rows} = props;
const refs = Array.from({length: rows.length}, a => React.useRef<HTMLButtonElement>(null));

然后尝试在每个map上使用button函数内部:

<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.useRef”不能在回调内调用。反应   挂钩必须在React函数组件或自定义React中调用   挂钩函数react-hooks / rules-of-hooks

如何动态执行此操作,以便可以在map函数中使用引用。我已经尝试了答案中的建议,但无法正常工作。 这是codesandbox of the example

2 个答案:

答案 0 :(得分:1)

这是另一种选择:

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)}
    />
}

答案 1 :(得分:0)

useRef与React.createRef不完全相同。 最好将其称为useInstanceField:)

因此,您的代码可能会有所不同。

第一步:我们使用useRef保存引用数组:

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中对其进行处理以更改裁判的长度

您还可以使用另一种方式:

1)创建一个引用数组,但没有React.createRef:

const {rows} = props;
const refs = useRef(new Array(rows.length));

在地图中,我们使用ref={el => refs.current[index] = el}存储参考

<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>