I am trying to implement a List view in React. What I am trying to achieve is that to store the list headers informations and register the components and register the scroll event. every time when user scroll the window, I'd like to take out the stored div and re-calculate the offsetTop
data.
The problem now is that, I found the console just print out the initial value (the value is fixed and never changed) offsetTop
data never change in onscroll
function.
Anyone suggest how to get latest offsetTop
from the _instances
object?
import React, { Component } from 'react'; import ListHeader from './lib/ListHeader'; import ListItems from './lib/ListItems'; const styles = { 'height': '400px', 'overflowY': 'auto', 'outline': '1px dashed red', 'width': '40%' }; class HeaderPosInfo { constructor(headerObj, originalPosition, originalHeight) { this.headerObj = headerObj; this.originalPosition = originalPosition; this.originalHeight = originalHeight; } } export default class ReactListView extends Component { static defaultProps = { events: ['scroll', 'mousewheel', 'DOMMouseScroll', 'MozMousePixelScroll', 'resize', 'touchmove', 'touchend'], _instances:[], _positionMap: new Set(), _topPos:'', _topWrapper:'' } static propTypes = { data: React.PropTypes.array.isRequired, headerAttName: React.PropTypes.string.isRequired, itemsAttName: React.PropTypes.string.isRequired, events: React.PropTypes.array, _instances: React.PropTypes.array, _positionMap: React.PropTypes.object, _topPos: React.PropTypes.string, _topWrapper: React.PropTypes.object }; state = { events: this.props.events, _instances: this.props._instances, _positionMap: this.props._positionMap, _topPos: this.props._topPos } componentDidMount() { this.initStickyHeaders(); } componentWillUnmount() { } componentDidUpdate() { } refsToArray(ctx, prefix){ let results = []; for (let i=0;;i++){ let ref = ctx.refs[prefix + '-' + String(i)]; if (ref) results.push(ref); else return results; } } initHeaderPositions() { // Retrieve all instance of headers and store position info this.props._instances.forEach((k)=>{ this.props._positionMap.add(new HeaderPosInfo( k, k.refs.header.getDOMNode().offsetTop, k.refs.header.getDOMNode().offsetHeight )); }); let it = this.props._positionMap.values(); let first = it.next(); this.props._topPos = first.value.originalPosition; this.props._topWrapper = first.value.headerObj; } initStickyHeaders () { this.props._instances = this.refsToArray(this, 'ListHeader'); this.initHeaderPositions(); // Register events listeners with the listview div this.props.events.forEach(type => { if (window.addEventListener) { React.findDOMNode(this.refs.listview).addEventListener(type, this.onScroll.bind(this), false); } else { React.findDOMNode(this.refs.listview).attachEvent('on' + type, this.onScroll.bind(this), false); } }); } onScroll() { // update current header positions and apply fixed positions to the top one console.log(1); let offsetTop = React.findDOMNode(this.props._instances[0].refs.header).offsetTop; } render() { const { data, headerAttName, itemsAttName } = this.props; let _refi = 0; let makeRef = () => { return 'ListHeader-' + (_refi++); }; return ( <div ref="listview" style={styles}> { Object.keys(data).map(k => { const header = data[k][headerAttName]; const items = data[k][itemsAttName]; return ( <ul key={k}> <ListHeader ref={makeRef()} header={header} /> <ListItems items={items} /> </ul> ); }) } </div> ); } }
The whole source code is on Github, you can clone and compile it from here:
Github
To get a div's offsetTop positions in React, we can get it from the ref assigned to the element. to assign the inputRef to the input. Then we get the offsetTop property from the inputRef. current property which has the input element.
Getting the Size and Position You can use Element. getClientRects() and Element. getBoundingClientRect() to get the size and position of an element. In React, you'll first need to get a reference to that element.
To get the position of an element with React Native, we can get it from the View 's onLayout callback. to add a View with the onLayout prop set to a function that gets the position values from the event. nativeEvent. layout property.
To get an element by ID in React: Set the ref prop on the element. Use the current property to access the element in the useEffect hook.
You may be encouraged to use the Element.getBoundingClientRect() method to get the top offset of your element. This method provides the full offset values (left, top, right, bottom, width, height) of your element in the viewport.
Check the John Resig's post describing how helpful this method is.
I do realize that the author asks question in relation to a class-based component, however I think it's worth mentioning that as of React 16.8.0 (February 6, 2019) you can take advantage of hooks in function-based components.
Example code:
import { useRef } from 'react' function Component() { const inputRef = useRef() return ( <input ref={inputRef} /> <div onScroll={() => { const { offsetTop } = inputRef.current ... }} > ) }
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With