React body scroll lock issue on IOS

1.8k Views Asked by At

I'm literally fighting in finding a clean solution to the scroll issue in the IOS devices. In my App.js i've simply the background body and a modal with some contents. When the modal is shown i'd like to block the scroll in the background (myBodyContent) and still let the scroll in the modal component. I'm quite new to both javascript and React and this not helping me at all.

The cleanest solution (according to me) i was able to find is the body-scroll-lock package but it seems i'm not able to successfully use it. here is my code:

App.js

class App extends Component {

      targetRef = React.createRef();
      targetElement = null;
    
      constructor(props) {
        super(props);
      }
      
      componentDidMount() {    
            this.targetElement = this.targetRef.current;
            disableBodyScroll(this.targetElement);
      } 

render() {

    const myModal = (
        <Modal ref={this.targetRef}>
          // my long content here
        </Modal>);

return (
      <React.Fragment>
        {myModal} 
          <Layout>
            <myBodyContent>
          </Layout>
      </React.Fragment>
    );
  }
}

Modal.js

class Modal extends Component {


    shouldComponentUpdate(nextProps, nextState){
        return (nextProps.show !== this.props.show)
    }

    render () {
        return (
            <div>
            <Auxi>
                <Backdrop
                    show = {this.props.show}
                    clicked = {this.props.modalClosed}
                />
                <div className={style.Modal}
                style={{
                    transform: this.props.show ? 'translateY(0)' : 'translateY(-100vh)', // vh is special unit for outside screen
                    opacity: this.props.show ? '1': '0'
                }}>
                    {this.props.children}
                </div>
            </Auxi>
            </div>
        );
    }
}

Modal css

.Modal {
    position: fixed;
    z-index: 500;
    background-color: white;
    width: 80%;
    overflow-y: hidden;
    overflow: auto;
    padding-right: 15px; /* Avoid width reflow */
    border: 1px solid #ccc;
    box-shadow: 1px 1px 1px black;
    padding: 16px;
    top: 5%;
    left: 5%;
    box-sizing: content-box;
    transition: all 0.3s ease-out;
}

@media (min-width: 600px) {
    .Modal {
        width: 80%;
        height: 80%;
        left: 10%;
        top: 10%
    }
}

With the above code, simply everything is locked and i cannot scroll neither the modal nor the myBodyContent.

Can you help me understanding what i'm doing wrong? Or suggest me some other ways to achieve the same result?

Thanks in advance for your help.

2

There are 2 best solutions below

0
On

You don't have targetElement (it's null) inside App componentDidMount because you try to set ref for React component but not HTML element.

To fix this you need to forward ref inside Modal component like that:

const myModal = (
  <Modal forwardedRef={this.targetRef}>
    // my long content here
  </Modal>
);

and then :

class Modal extends Component {


    shouldComponentUpdate(nextProps, nextState){
        return (nextProps.show !== this.props.show)
    }

    render () {
        return (
            <div ref={this.props.forwardedRef}>
            <Auxi>
                <Backdrop
                    show = {this.props.show}
                    clicked = {this.props.modalClosed}
                />
                <div className={style.Modal}
                style={{
                    transform: this.props.show ? 'translateY(0)' : 'translateY(-100vh)', // vh is special unit for outside screen
                    opacity: this.props.show ? '1': '0'
                }}>
                    {this.props.children}
                </div>
            </Auxi>
            </div>
        );
    }
}
0
On

Thanks Max, i've tried but unfortunately the result is the same. I've also tried to enclose the Modal in a div directly in the App.js and apply the ref directly there without passing it as props...but it's the same. No way to scroll anything.