How to scroll to element of the list in other component?
class Posts extends Components {
render() {
<ul>
{this.props.posts.map((post) =>
<li key={post.id}>{post.title}</li>
)}
</ul>
}
//redux
}
class OtherComponent extends Components {
onClickHandler = (id) => {
//!!!!!!!!
//scroll page to li with key={post.id}
}
render() {
<div>
{this.props.posts.map((post) =>
<div key={post.id} onClick={() => this.onClickHandler(post.id)}>{post.title}</div>
)}
</div>
}
//redux
}
I found solutions here: ReactJS how to scroll to an element
but all are for single element.
If I have list, I need to create a reference to everyone? The list of posts can change, so I can not do it.
How to scroll to element of the list in other component?
class Posts extends Components {
render() {
<ul>
{this.props.posts.map((post) =>
<li key={post.id}>{post.title}</li>
)}
</ul>
}
//redux
}
class OtherComponent extends Components {
onClickHandler = (id) => {
//!!!!!!!!
//scroll page to li with key={post.id}
}
render() {
<div>
{this.props.posts.map((post) =>
<div key={post.id} onClick={() => this.onClickHandler(post.id)}>{post.title}</div>
)}
</div>
}
//redux
}
I found solutions here: ReactJS how to scroll to an element
but all are for single element.
If I have list, I need to create a reference to everyone? The list of posts can change, so I can not do it.
Share Improve this question asked Sep 18, 2018 at 19:04 tolutolu 691 gold badge1 silver badge5 bronze badges 2- How those 2 components are connected? – Arup Rakshit Commented Sep 18, 2018 at 19:29
- By redux, but in app both are in the same "level" – tolu Commented Sep 18, 2018 at 19:31
3 Answers
Reset to default 11You can build an index of refs
based on the key of the item when iterating over the list, then use that key to lookup the ref and invoke scrollIntoView
on the element.
UPDATE: Revised code example now shows how to store the list of refs in a child component and invoke a function on that child component to scroll to a particular item in the list. Using refs, you can call a function on a child component instance. This isn't ideal since it breaks out of React's declarative model, but in some situations, particularly around interacting directly with DOM elements for focus or scrolling, it is needed. If you haven't already I also suggest reading the brief React guide to Refs and the DOM, as well as Forwarding Refs and how they may be helpful.
const items = Array.from(new Array(1000).keys()).map(m => ({
id: m + 1,
name: `Item ${m + 1}`
}));
const ComponentA = ({ children, onClick }) => (
<button type="button" onClick={onClick}>
{children}
</button>
);
class ComponentB extends React.Component {
constructor(props, context) {
super(props, context);
this.itemRefs = {};
}
scrollTo(id) {
this.itemRefs[id].scrollIntoView();
}
render() {
return (
<ul>
{items.map(m => (
<li key={m.id} ref={el => (this.itemRefs[m.id] = el)}>
{m.name}
</li>
))}
</ul>
);
}
}
class App extends React.Component {
render() {
return (
<div>
<ComponentA onClick={() => this.listComponent.scrollTo(1000)}>
Jump to Item 1000
</ComponentA>
<ComponentB ref={e => (this.listComponent = e)} />
<ComponentA onClick={() => this.listComponent.scrollTo(1)}>
Jump to Item 1
</ComponentA>
</div>
);
}
}
const rootElement = document.getElementById("root");
ReactDOM.render(<App />, rootElement);
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/15.1.0/react.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/15.1.0/react-dom.min.js"></script>
<div id="root"></div>
I'm adding another solution here, as the accepted answer is a bit too complicated.
All you need is a ref to the li you want to scroll to.
Using react hooks it's very simple.
const ScrollingList = () => {
const ref = useRef();
const scroll = () =>
ref?.current?.scrollIntoView({ behavior: "smooth" });
return (
<div>
/* other component - button */
<button onClick={scroll}>Scroll to Last element</button>
<ul>
{list.map((item) => <li>{item}</li>)}
<li ref={ref}>Last Element</li>
</ul>
</div>
);
};
Code Explanation
This is a simple React function component.
You can replace the button component with any other custom component, and pass the scroll function as a prop.
You can also specify options object to the scrollIntoView like I did - all the props of the options object can be found in the Web Api docs.
This is a little improvement over @Tyler's answer. You can just assign ref to the parent element and use js child selector to get the child element and scroll that into the viewport.
Most of the use cases might involve scolling to top of the list and bottom of the list. in that case you can just use
this.listRef.current.firstChild.scrollIntoView()
and this.listRef.current.lastChild.scrollIntoView()
respectively.
Checkout the snippet below.
const items = Array.from(new Array(1000).keys()).map(m => ({
id: m + 1,
name: `Item ${m + 1}`
}));
const ComponentA = ({ children, onClick }) => (
<button className="btn m-2" type="button" onClick={onClick}>
{children}
</button>
);
class ComponentB extends React.Component {
constructor() {
super();
this.listRef = React.createRef();
}
scrollTo(id){
this.listRef.current.children.item(id - 1).scrollIntoView({
behavior: "smooth",
})
// if only firstChild/lastChild is what to want to scrollTo.
// this.listRef.current.firstChild.scrollIntoView()
// this.listRef.current.lastChild.scrollIntoView()
}
render() {
return (
<ul className="list-group" ref={this.listRef}>
{items.map(m => (
<li className="list-group-item" key={m.id}>
{m.name}
</li>
))}
</ul>
);
}
}
class App extends React.Component {
render() {
return (
<div>
<div>
<ComponentA onClick={() => this.listComponent.scrollTo(655)}>
Jump to Item 655
</ComponentA>
<ComponentA onClick={() => this.listComponent.scrollTo(999)}>
Jump to Item 999
</ComponentA>
<ComponentA onClick={() => this.listComponent.scrollTo(1)}>
Jump to Item 1
</ComponentA>
</div>
<div>
<ComponentB ref={e => (this.listComponent = e)} />
</div>
</div>
);
}
}
const rootElement = document.getElementById("root");
ReactDOM.render(<App />, rootElement);
<script src="https://unpkg.com/react@17/umd/react.development.js" crossorigin></script>
<script src="https://unpkg.com/react-dom@17/umd/react-dom.development.js" crossorigin></script>
<link rel="stylesheet"crossorigin href="https://unpkg.com/[email protected]/dist/css/bootstrap.min.css"></link>
<div id="root"></div>