I have a React app where I put a progress bar which changes its value once the user reaches some DOM elements. To build that feature I referred to window object with its 'scroll' properties:
// appointment.js
class Appointment extends React.Component {
state = {
progBarWidth: 33
}
checkCoordinates() {
const stepTwoRect = document
.querySelector('#stepTwo')
.getBoundingClientRect().top
const stepThreeRect = document
.querySelector('#stepThree')
.getBoundingClientRect().top
if (window.scrollY >= stepTwoRect && window.scrollY < stepThreeRect) {
this.setState({ progBarWidth: 66 })
}
else if (window.scrollY < stepTwoRect) {
this.setState({ progBarWidth: 33 })
}
else if (window.scrollY >= stepThreeRect) {
this.setState({ progBarWidth: 100 })
}
}
componentDidMount() {
window.addEventListener(
'scroll',
this.checkCoordinates.bind(this)
)
}
componentWillUnmount() {
window.removeEventListener(
'scroll',
this.checkCoordinates.bind(this)
)
}
render() {
return (
<React.Fragment>
<div
className="progress position-fixed"
>
<div
className="progress-bar"
style={{
width: `${this.state.progBarWidth}%`,
}}
aria-valuenow="33"
aria-valuemin="0"
aria-valuemax="100"
></div>
</div>
<h1 id="greeting"> hello ! </h1>
<h1>
Very long Lorem Ipsum to fill the page
</h1>
<h1 id="stepTwo"> Step 2 </h1>
<h1>
Again very long Lorem Ipsum
</h1>
<h1 id="stepThree"> Step 3 </h1>
<h1>
Very long Lorem Ipsum
</h1>
</div>
<div>
)
}
}
The logic is that as I said once a user reaches #stepTwo, the progress bar should be filled by 66% in contrast to initial 33%, and once the user reaches #stepThree, it should be filled by 100%.
checkCoordinates does this function changing the state, and then that value is sent to width style property of progress bar.
I also made a bind for that function when adding event listener because it killed couple of my hours.
I think the problem is either in using document so when I go to other routes the app cannot reach #stepTwo and #stepThree (which are included only in appointment.js, however React puts everything in the root in index.html in the end). Or it is binding.
I tested in different browsers and tried to rerun the application however nothing helped me.
I also get
TypeError: Cannot read property 'getBoundingClientRect' of null
Appointment.checkCoordinates
src/Appointment/Appointment.js:36
33 | // }
34 |
35 | checkCoordinates() {
> 36 | const stepTwoRect = document
| ^ 37 | .querySelector('#stepTwo')
38 | .getBoundingClientRect().top
39 |
together with 'can't find querySelector of null'.
Any thoughts?
P.S. when I just close the error by pushing X, everything works until I scroll until the very end of the page.
P.P.S. I noticed that when I use sass files for an individual component, styles apply to all the application components, not only the one for which it was created. I guess probably my app seeks for #stepOne on every page scroll, and when it cannot find, it throws an error..
P.P.P.S. I tried to bind the function once - just in the constructor. Now I have a problem with setState
question from:
https://stackoverflow.com/questions/65829013/after-referring-to-document-by-queryselector-i-get-an-error-in-my-react-app-ev 与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…