Reputation: 83
I am trying to access a specific point on DOM using useRef() from a dropdown.
at the moment I have implemented the logic using #
ID but I want to use useRef().
Also to be mentioned: I don't have access to the App.js to
pass the ref
to other components.
dropdown.js
export default function DropDown() {
const ScrollToTitle = (e) => {
const title = e.target.value;
window.location = '#' + title;
// how to access the ref in here insted of # ID?
};
return (
<select onChange={ScrollToTitle}>
<option>First </option>
<option>Second </option>
<option>Third </option>
<option>Another </option>
<option>Somthing </option>
</select>
);
}
I have a couple of titles on the App but as an example here is one of them:
export default function First() {
const titleRef = useRef(); // what I wan to use
return (
<div id='First'>
<h1 ref={titleRef}>First </h1> // is this correct ?
<p>Start editing to see some magic happen :)</p>
<p>Start editing to see some magic happen :)</p>
</div>
);
}
How and from where to access the titleRef
?
to be mentioned again : I don't have access to the App.js to
pass the ref
to other components.
and I have a live example here: https://stackblitz.com/edit/react-cpezfb
Thanks a lot
Upvotes: 0
Views: 946
Reputation: 1877
You can scroll to an element using its ref (such as titleRef
) by doing the following:
titleRef.current.scrollIntoView();
A more complete solution to your particular issue is here, where I updated your code to work for the First
and Second
components.
The idea is that the refs to the components you want to scroll to should be shared, thus defined in the parent. The refs should be assigned to the different components, then the dropdown can make use of them for the navigation.
Ideally, you should avoid the use of the strings "First", "Second" etc as keys and use constants for instance (by defining the values as constant such as const FIRST = "First";
then using those constants so that your code is easier to maintain.
Upvotes: 1
Reputation: 15540
Your problem with useRef
is you did not share the mutual states between section components and the dropdown list component, so if you want to call useRef
, you need to share that with all your related components.
Here is the full implementation of how to call useRef
for your case
Firstly, I used forwardRef
to pass the ref to a specific element in your section components (in this case, the ref is the title element). If you don't understand what it is, you can read this doc
import React, { useRef } from 'react';
export default React.forwardRef(function First(props, ref) {
return (
<div id='First'>
<h1 ref={ref}>First </h1>
<p>Start editing to see some magic happen :)</p>
<p>Start editing to see some magic happen :)</p>
<p>Start editing to see some magic happen :)</p>
<p>Start editing to see some magic happen :)</p>
<p>Start editing to see some magic happen :)</p>
<p>Start editing to see some magic happen :)</p>
<p>Start editing to see some magic happen :)</p>
<p>Start editing to see some magic happen :)</p>
<p>Start editing to see some magic happen :)</p>
<p>Start editing to see some magic happen :)</p>
</div>
);
})
And then in App.js
, I defined useRef
for a list of refs (not single) for all sections. I also used liftting state up technique for scrollToTitle
import React, { useRef } from 'react';
import './style.css';
import First from './components/First';
import Second from './components/Second';
import Third from './components/Third';
import Another from './components/Another';
import Somthing from './components/Somthing';
import DropDown from './components/DropDown';
export default function App() {
const titleRefs = useRef({});
const scrollToTitle = (e) => {
const title = e.target.value;
titleRefs.current[title].scrollIntoView();
};
return (
<div>
<h1>Hello StackBlitz!</h1>
<DropDown scrollToTitle={scrollToTitle} />
<First ref={(node) => (titleRefs.current['First'] = node)} />
<Second ref={(node) => (titleRefs.current['Second'] = node)} />
<Third ref={(node) => (titleRefs.current['Third'] = node)} />
<Another ref={(node) => (titleRefs.current['Another'] = node)} />
<Somthing ref={(node) => (titleRefs.current['Somthing'] = node)} />
</div>
);
}
BUT I think you used window.location = '#' + title;
is not that bad. With the above implementation, your URL won't have #id
to access that section directly by URL, so many of us still prefer using #
instead of ref
.
Upvotes: 1