I have a table
list where each row has a menu
button, for which I need a ref
. I am using react mui in my project and it's menu. I have tried creating the refs like this:
const {rows} = props;
const refs = Array.from({length: rows.length}, a => React.useRef<HTMLButtonElement>(null));
And then tried to use the inside the map
function like this on each 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>
But, then I get an error:
React Hook "React.useRef" cannot be called inside a callback. React Hooks must be called in a React function component or a custom React Hook function react-hooks/rules-of-hooks
How can I do this dynamically, so that I can use refs inside the map function. I have tried with the suggestion in the answers, but I couldn't get it to work. Here is the codesandbox of the example.
Here another option:
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)}
/>
}
useRef
is not exactly the same as React.createRef.
it's better to call it useInstanceField
:)
So, your code could be a bit another.
First step: we use useRef
to save the array of refs:
const {rows} = props;
const refs = useRef(Array.from({length: rows.length}, a => React.createRef()));
Then, in your map function we save each ref to its index in the refs array:
<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>
if your length is changed, you should process it in useEffect
to change the length of refs
You also can use another way:
1) Create an array of refs, but without React.createRef:
const {rows} = props;
const refs = useRef(new Array(rows.length));
In map we use ref={el => refs.current[index] = el}
to store 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>
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With