2015-09-19 32 views
18

Próbuję zaimplementować widok listy w React. Próbuję osiągnąć to, że przechowuję informacje o nagłówkach list, rejestruję komponenty i rejestruję zdarzenia przewijania. za każdym razem, gdy użytkownik przewinie okno, chciałbym wyjąć przechowywane div i ponownie obliczyć dane offsetTop.Uzyskaj przesunięcie div Top pozycje w React

Problem polega na tym, że znalazłem konsolę po prostu wydrukować wartość początkową (wartość jest stała i nigdy się nie zmienia) offsetTop dane nigdy nie zmieniają się w funkcji .

Ktoś sugeruje, jak uzyskać najnowszy offsetTop z obiektu _instances?

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> 
    ); 
    } 
} 

Kod cały źródłem jest na Github, można sklonować i skompilować go stąd:

Github

+0

Prawdopodobnie musisz dodać wartość '' 'scrollTop''' do przesunięcia w celu uzyskania" rzeczywistego "przesunięcia. Nie mogłem uzyskać kodu na github do pracy, więc nie mogłem tego spróbować:/ –

+0

@PatrickNeschkudla naprawdę? jakie masz błędy? prawdopodobnie potrzebujesz najpierw zainstalować pakiet sieciowy. – JavaScripter

+0

Tylko FYI dla wszystkich, którzy się o to potknęli, pamiętajcie, że React przesunął 'React.findDOMNode' do własnego pakietu' react-dom'. Zobacz [tutaj] (https://facebook.github.io/react/docs/top-level-api.html#reactdom.finddomnode) – aug

Odpowiedz

36

Możesz być zachęcani do korzystania z metody Element.getBoundingClientRect() aby górna przesunięcie elemencie . Ta metoda zapewnia pełne wartości odsunięcia (lewy, górny, prawy, dolny, szerokość, wysokość) elementu w oknie roboczym.

Sprawdź, czy John Resig's post jest pomocny w przypadku tej metody.

+1

Chciałbym podkreślić, że ta metoda powoduje znaczne obniżenie wydajności w porównaniu z uzyskaniem sprawiedliwego przesunięcie. – vise

+0

Czy możesz udzielić lepszej odpowiedzi na to pytanie za pomocą metody offsetTop lub czegoś innego, co getBoundingClientRect? – TheJKFever

+0

Podczas łączenia z zewnętrznymi adresami URL dodaj treść lub podsumowanie, na wypadek gdyby adres URL przestał być poprawny w przyszłości. –

8

odpowiedź Eugeniusza wykorzystuje właściwą funkcję, aby uzyskać dane, ale dla potomności Chciałbym sformułować dokładnie jak go używać w React v0.14 + (wg this answer):

import ReactDOM from 'react-dom'; 
    //... 
    componentDidMount() { 
    var rect = ReactDOM.findDOMNode(this) 
     .getBoundingClientRect() 
    } 

Is działa idealnie dla mnie i używam danych, aby przewinąć do góry nowego zamontowanego komponentu.

+0

Ta odpowiedź zadziałała dla mnie 2 lutego 2018 roku. – agm1984

3

Lepsze rozwiązanie z ref do uniknięcia findDOMNode, które jest odradzane.

... 
onScroll() { 
    let offsetTop = this.instance.getBoundingClientRect().top; 
} 
... 
render() { 
... 
<Component ref={(el) => this.instance = el } /> 
...