Search code examples
reactjsstyled-componentsgatsby

Why is my hacky use of Styled Components causing the call stack size to be exceeded


Am making a website using Gatsby. Am using styled components as well.

For a certain page the designers in their infinite wisdom have decided that a certain button should 1) SHow at the top of the page for desktop 2) Show at the bottom of the page for mobile. Since I am already deep into the project I did not want to go about positioning everything with absolute. As other things will break. So I tried this.

const SizeDetector = styled.div`
  height: 0px;
  width: 100%;
  background-color: ${props => {
                        props.detectDesktopAndTablet()
                        return 'white'
                        }
                      }
  @media (max-width: 450px) {
    background-color: ${props => {
                          props.detectMobile()
                          return 'white'
                          }
                        }
                      }
`

class ContextualFrame extends React.Component {
  constructor() {
    super();
    this.detectMobile = this.detectMobile.bind(this);
    this.detectDesktopAndTablet = this.detectDesktopAndTablet.bind(this);
    this.detectDesktopAndTablet = this.detectDesktopAndTablet.bind(this);
    this.state = {'mobile': false, 'hasMounted': false}
  }
  detectMobile() {
    this.setState({'mobile': true})
  }
  detectDesktopAndTablet() {
    console.log('here')
    this.setState({'mobile': false})
  }
  componentDidMount() {
    this.setState({'hasMounted': true})
  }
  render() {
    let {mobile, hasMounted} = this.state
    return (
      <div>
        {
          hasMounted ? (<SizeDetector detectDesktopAndTablet={this.detectDesktopAndTablet} detectMobile={this.detectMobile} />) : null
        }
        {
          mobile ? null : (<MeetPeopleButton text={'Meet The Team'} />)
        }
        {this.props.children}
        {
          mobile ? (<MeetPeopleButton text={'Meet The Team'} />) : null
        }
      </div>
    )
  }
}

This is causing an error:

RangeError: Maximum call stack size exceeded

The 'here' in the console log in the detectDesktopAndTablet is being called thousands of times.

Why is this happening? I guess that this might be happening due to some particular way that either Styled Components or React work. Asking to increase my understanding of how React works.


Solution

  • Maybe I've figured out your problem. It is that during the render, you are changing your component's state, making it render again, and so on:

    ContextualFrame.render() -> calls SizeDetector.render() -> calls detectDesktopAndTablet() -> calls setState(...) -> procs ContextualFrame.render()

    To fix this, checks if the property is the same before setting it:

    detectDesktopAndTablet() {
      console.log('here')
      if(this.state.mobile !== false) this.setState({'mobile': false})
    }