I am using a <NavLink />
to direct navigation to a specific component. I am thinking this is the problem - but the problem is:
I am trying to have an element focused when the route is fulfilled and the component renders. The existing approach I have somewhat works - the scroll works (i.e., the element I want focused is near the bottom and the automatic scroll to works. However, the focus box is not there). When I refresh, however, it works as desired - the focus has the scroll to functionality and there is a highlighted box around the element.
I am not sure what the issue is but I am thinking it is the fact that component gets its initial render from the <NavLink />
.
Here is how I am using the focus:
import React, { useEffect, useRef } from "react";
const Focus = () => {
const focusRef = useRef(null);
useEffect(() => {
if (focusRef && focusRef.current) {
focusRef.current.focus();
}
}, [focusRef.current]);
return (
<React.Fragment>
<h1 tabIndex="-1" ref={focus}>
Focus Here
</h1>
</React.Fragment>
);
};
Any help is greatly appreciated!
CodePudding user response:
The problem you are facing is that changing a ref does not trigger a render. You are using focusRef.current
as a dependency of your useEffect
hook, but in fact, when the current value of focusRef
changes, it doesn't necessarily trigger the effect, it simply mutates the ref only. So then you need something else to trigger the re-rendering and when that happens a change of the focusRef.current
is detected and effect can re-run. And if focusRef.current
never changes, the effect is only even trigger when the component mounts (this is why it works on refresh).
You are probably using the wrong tool to do the job. Ask yourself what you want to component to response to. Changing location? Then you need an effect to run based on location change, not a ref.
const focusRef = useRef()
const location = useLocation()
useEffect(() => {
if (location.pathname === '/my-url') {
focusRef.current.focus()
}
}, [location])
Something like this triggers the effect on every change of location
, then, inside the effect, you can put your condition for a pathname for example.
CodePudding user response:
A ref is a reference to an object with a single property - current
. Setting the current property doesn't change the object, or cause a re-render.
To circumvent that, you can use useState
to hold the ref. A ref in react can also be a function. Pass the setFocusRef
to the component, which will call it as soon as it renders. This will set focusRef
to point at the relevant DOM element, and cause a re-render. The changed value of focusRef
would trigger the useEffect
, and since the focusRef
is not null
now, focusRef.focus
would be called.
const { useState, useEffect } = React;
const Focus = () => {
const [focusRef, setFocusRef] = useState(null);
useEffect(() => {
if(!focusRef) return;
focusRef.focus();
}, [focusRef]);
return (
<h1 tabIndex="-1" ref={setFocusRef}>
Focus Here
</h1>
);
};
ReactDOM.render(
<Focus />,
root
)
<script crossorigin src="https://unpkg.com/react@17/umd/react.development.js"></script>
<script crossorigin src="https://unpkg.com/react-dom@17/umd/react-dom.development.js"></script>
<div id="root"></div>