How to Scroll to a Component by Clicking a Button with React
Tags: react
Published: 2022-10-25 08:50:58

Imagine you have a really long form with dynamically generated fields, and you have to scroll to an invalid field message by clicking the Submit button. All the form fields are independent and are not managed by any form library.


![The example form](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/r0bsb7x0dy625bm29zjc.png)

##### Expected behavior

![Expected behavior](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/504aff8gszhtmj0hg0xq.gif)

### Solution #1

The first thing that comes to my mind is to use native API `element.scrollIntoView()` or `Window.scrollTo()`, but the critical question is how to find the target element to pass it or its coordinates to the scroll function?

The second thing comes to my mind is to find this element by ID using `document.querySelector()` or `document.getElementById()`. So, the code will look something like that:

```tsx
import {validateFormField, formFieldId} from "../form-field"

const handleFormSubmit = (e) => {
    e.preventDefault(); // prevent form submit
    const isUniqueFieldValid = validateFormField();
    if (!isUniqueFieldValid) {
        const uniqueField = document.getElementById(formFieldId);
        uniqueField?.scrollIntoView();
        return;
    }
}
```

### Issue

But did you mention that the suggested approach breaks the core React principles – the concept of the components. We violate this principle when trying to turn to something inside the component (form field) from the business logic. This time we are starting to manipulate DOM directly in a jQuery-like style.

### Solution #2

To avoid this dirty trick, I propose returning to the components approach and encapsulating scrolling logic inside the form field component. Thus, by clicking on submit button, I don't need to find the element and scroll into it. I need to dispatch the event to notify that element should be shown:

```tsx
import {validateFormField} from "./business-logic/validate-form-field";

const handleSubmit = (e) => {
    e.preventDefault();
    // ⚠️ complex business logic to check if <InputField /> is invalid
    const isUniqueFieldValid = validateFormField();
    if (!isUniqueFieldValid) {
        emitErrorInFormField();
    }
}
```

By dispatching the event, we could resolve the issue of UI-logic encapsulation. Inside the component, we could legally apply to elements and call functions like `element.scrollIntoView()` because we may use `ref's from React API, which gives us a link to the DOM node.

In a simple React/Redux application without an event bus, the easiest way to trigger the scroll inside the `<FormField />` component is to switch the prop `shouldScrollToElement` to `true` and switch it to `false` immediately`.

```tsx
import React, {useEffect, useRef} from "react";

export const FormField = (props: {
    shouldScrollToElement?: boolean, errorMessage?: string, onScrollToElement?: () => void
}) => {
    const {shouldScrollToElement, errorMessage, onScrollToElement} = props;
    const ref = useRef<HTMLDivElement>(null);

    useEffect(() => {
        if (shouldScrollToElement) {
            ref?.current?.scrollIntoView();
            onScrollToElement?.();
        }
    }, [shouldScrollToElement, onScrollToElement])

    return (<div ref={ref}>
        <label />
        <input />
        {errorMessage ? (<div>{errorMessage}</div>) : null}
    </div>)
}
```

_____________________________

### Demo

{% embed https://stackblitz.com/edit/github-8dplrl?file=src/App.tsx %}

_____________________________

### Conclusion

To sum up, let's repeat the key steps to implement the suggested solution:
* Invoke the validation function by clicking the 'Submit' button
* If there are some validation errors, dispatch the action
* The action changes prop `shouldScrollToElement` of `<FormField />` component
* `<FormField />` handles prop change. We get an element to scroll using React ref and invoke the `.scrollIntoView()` method on this element
* Immediately after the scrolling, the component should dispatch the event to reset prop `shouldScrollToElement`

This is a simple example of scrolling implementation in React applications that does not break the component encapsulation principle.


Cheers!