React-window: Kompatibilitas dengan ScrollSync

Dibuat pada 8 Nov 2018  ·  52Komentar  ·  Sumber: bvaughn/react-window

Saya memiliki kasus penggunaan di mana saya perlu membuat Grid yang memiliki header tetap yang tetap disematkan ke atas. Dalam reaksi-virtualisasi ini dapat dicapai dengan membuat dua komponen kisi (satu untuk tajuk, satu untuk badan kisi) dan menyinkronkan posisi gulir sehingga pengguliran horizontal kisi tajuk disinkronkan dengan kisi utama.

Saya tahu bahwa Anda mencoba untuk menjaga bobot reaksi-jendela lebih ringan, baik dalam hal ukuran bundel dan kompleksitas konseptual, demi membangun fungsionalitas tambahan sebagai paket terpisah. Itu adalah arah yang saya setujui. Saya pikir komponen ScrollSync dari react-virtualized dapat diekstraksi atau diadaptasi untuk bekerja dengan react-window. Namun, untuk melakukannya, jendela reaksi perlu menerima alat peraga untuk scrollLeft dan scrollTop sehingga offset gulir dari kisi header dapat dikelola secara langsung.

Apakah ini kasus penggunaan yang ingin Anda dukung? Jika tidak, apakah Anda punya saran ke arah mana saya harus menerapkan ini sendiri?

Terima kasih untuk Anda bekerja di perpustakaan ini. Sebagai seseorang yang telah menggunakan reaksi-virtualisasi selama beberapa tahun, saya menghargai kesederhanaan memulai dengan reaksi-jendela dan bagaimana kinerjanya bagi saya tanpa banyak intervensi manual.

Komentar yang paling membantu

Itu masuk akal. Terima kasih untuk sarannya! Saya membuatnya berfungsi, dan sebenarnya cukup mudah diatur. Sangat mudah sehingga tidak menjamin paket mandiri. Mungkin contoh di dokumen, tapi itu panggilan Anda, saya kira. Saya akan meninggalkan tautan ke contoh Kode Sandbox yang berfungsi di sini jika ada orang lain yang menemukan masalah ini di masa mendatang.

https://codesandbox.io/s/y3pyp85zm1

TLDR - letakkan referensi di grid header, dan letakkan ini di grid body.

onScroll={({ scrollLeft }) => this.headerGrid.current.scrollTo({ scrollLeft })}

Semua 52 komentar

Pertama, terima kasih atas kata-kata baik dan umpan balik positif. Saya senang mendengar bahwa jendela reaksi telah bekerja dengan baik untuk Anda sejauh ini!

Saya setuju bahwa komponen seperti ScrollSync dapat dirilis sebagai paket mandiri yang bergantung pada jendela reaksi, tetapi saya tidak ingin menambahkannya ke proyek ini karena ini bukan inti dari jendela.

Berkenaan dengan pertanyaan spesifik Anda tentang alat peraga gulir, ini bukan perubahan yang ingin saya lakukan pada proyek karena setelah menggunakannya dengan reaksi-virtualisasi, saya menyadari itu memiliki beberapa kelemahan serius. Saya sebenarnya menulis tentang itu di blog React jika Anda penasaran:

https://reactjs.org/blog/2018/06/07/you-probably-dont-need-derived-state.html#anti -pattern-erasing-state-when-props-change

Anda dapat menggunakan API gulir imperatif yang ditawarkan oleh jendela reaksi untuk mencapai perilaku sinkronisasi yang serupa. Anda hanya perlu memanggil metode ini dari siklus hidup komit daripada dengan melewati alat peraga.

Semoga ini masuk akal tetapi jangan ragu untuk mengajukan pertanyaan lanjutan jika tidak!

Itu masuk akal. Terima kasih untuk sarannya! Saya membuatnya berfungsi, dan sebenarnya cukup mudah diatur. Sangat mudah sehingga tidak menjamin paket mandiri. Mungkin contoh di dokumen, tapi itu panggilan Anda, saya kira. Saya akan meninggalkan tautan ke contoh Kode Sandbox yang berfungsi di sini jika ada orang lain yang menemukan masalah ini di masa mendatang.

https://codesandbox.io/s/y3pyp85zm1

TLDR - letakkan referensi di grid header, dan letakkan ini di grid body.

onScroll={({ scrollLeft }) => this.headerGrid.current.scrollTo({ scrollLeft })}

Terima kasih untuk tautannya! Itu sangat bijaksana.

Pada Kamis, 8 November 2018, 13:09 Reagan Keeler < [email protected] menulis:

Itu masuk akal. Terima kasih untuk sarannya! Saya membuatnya bekerja,
dan itu sebenarnya cukup mudah untuk diatur. Sangat mudah sehingga pasti tidak
menjamin paket mandiri. Mungkin contoh di dokumen, tapi itu milik Anda
panggilan saya kira. Saya akan meninggalkan tautan ke contoh Kode Sandbox yang berfungsi di sini
dalam kasus orang lain tersandung pada masalah ini di masa depan.

https://codesandbox.io/s/y3pyp85zm1

TLDR - letakkan referensi di grid header, dan letakkan ini di grid body.

onScroll={({ scrollLeft }) => this.headerGrid.current.scrollTo({ scrollLeft })}


Anda menerima ini karena Anda mengubah status buka/tutup.
Balas email ini secara langsung, lihat di GitHub
https://github.com/bvaughn/react-window/issues/86#issuecomment-437156749 ,
atau matikan utasnya
https://github.com/notifications/unsubscribe-auth/AABznTUunzEIs6bVQfVsz7T21L2-Pkkoks5utJ17gaJpZM4YVMd7
.

Bagi siapa pun yang menemukan ini, saya memiliki super grid yang disinkronkan dengan scroll atas/kanan/bawah/kiri di react-virtualized yang baru saja saya migrasikan ke lib ini. Saya akan mengatakan bahwa solusi di atas setidaknya berkinerja lebih baik jika tidak lebih baik, dan jauh lebih tidak menyakitkan daripada menggunakan ScrollSync di perpustakaan lain.

Itu juga berpasangan dengan cukup baik dengan kait reaksi useRef (https://reactjs.org/docs/hooks-reference.html#useref)

const topRef = useRef();
const rightRef = useRef();
const bottomRef = useRef();
const leftRef = useRef();
...
<Grid
  onScroll={({ scrollLeft, scrollTop }) => {
    if (leftRef.current) {
      leftRef.current.scrollTo({ scrollTop });
    }
    if (rightRef.current) {
      rightRef.current.scrollTo({ scrollTop });
    }
    if (topRef.current) {
      topRef.current.scrollTo({ scrollLeft });
    }
    if (bottomRef.current) {
      bottomRef.current.scrollTo({ scrollLeft });
    }
  }}
  ...
/>

Bagus! Terima kasih telah berbagi @ranneyd!

Itu juga berpasangan dengan cukup baik dengan kait reaksi useRef (https://reactjs.org/docs/hooks-reference.html#useref)

Terima kasih @ranneyd

Apakah mungkin untuk membagikan contoh kerja?

@ranneyd terima kasih lagi, saya mengikuti pendekatan Anda dan sinkronisasi gulir berfungsi dengan baik.

Luapan tersembunyi pada bilah gulir menyebabkan ketidaksejajaran di dekat ujung kisi:

gridalignement

Adakah saran tentang cara memperbaikinya?

Contoh CodeSandbox

Terima kasih sebelumnya

@carlosagsmendes itu karena bilah gulir. Di sebelah kiri buat tinggi menjadi tinggi dikurangi ukuran bilah gulir. Untuk mendapatkan konsistensi di seluruh perangkat, saya secara manual mengkodekan ukuran bilah gulir dengan CSS. Jika konten Anda sangat dinamis sehingga mungkin ada atau tidak ada bilah gulir, lakukan hal seperti "jumlah item * ukuran item < lebar (Anda harus memiliki semua nilai itu di sana)" dan kemudian ubah ketinggiannya tergantung pada itu.

Untuk mendapatkan konsistensi di seluruh perangkat, saya secara manual mengkodekan ukuran bilah gulir dengan CSS.

FWIW paket dom-helpers memiliki fungsi scrollbarSize berguna yang memberi tahu Anda berapa lebar perangkat saat ini. Mungkin lebih bagus daripada hard-coding.

@bvaughn ya! Saya lupa menyebutkan itu. Namun, itu tidak berhasil untuk kami. Saya pikir masalahnya adalah kami sudah melakukan bilah gulir yang dikodekan dengan keras dan itu membingungkannya

Terima kasih. Saya akan mencobanya!

Apakah ini berfungsi dengan VariableSizeList horizontal? Saya mencoba tetapi browser saya macet.

onScroll={({ scrollLeft }) => this.headerGrid.current.scrollTo({ scrollLeft })}

Ini bekerja dengan baik dengan VariableSizeGrid tetapi tajuk saya sedikit tertinggal saat menggulir.

@ajaymore jadi header tertinggal: sinkronisasi gulir kesulitan saat Anda menggunakan roda gulir di Mac. MacOSX memiliki pengguliran alami bawaan ini di mana ia menginterpolasi pengguliran untuk membuatnya "lebih halus". Kecepatan refresh pada ini sebenarnya lebih cepat daripada kecepatan bingkai animasi di Chrome. Dengan demikian, elemen yang Anda gulir akan dianimasikan sebelum V8/ScrollSync memiliki waktu untuk memperbarui DOM. Ini adalah batasan teknis yang belum saya lihat solusinya.

Fakta menyenangkan: jika Anda secara manual menggunakan bilah gulir di browser (seperti menggulir dengan cara lama dengan menyeret benda kecil) itu berfungsi dengan baik. Hal interpolasi dibangun ke dalam menggesek roda gulir / track pad

@ajaymore jadi header tertinggal: sinkronisasi gulir kesulitan saat Anda menggunakan roda gulir di Mac. MacOSX memiliki pengguliran alami bawaan ini di mana ia menginterpolasi pengguliran untuk membuatnya "lebih halus". Kecepatan refresh pada ini sebenarnya lebih cepat daripada kecepatan bingkai animasi di Chrome. Dengan demikian, elemen yang Anda gulir akan dianimasikan sebelum V8/ScrollSync memiliki waktu untuk memperbarui DOM. Ini adalah batasan teknis yang belum saya lihat solusinya.

Fakta menyenangkan: jika Anda secara manual menggunakan bilah gulir di browser (seperti menggulir dengan cara lama dengan menyeret benda kecil) itu berfungsi dengan baik. Hal interpolasi dibangun ke dalam menggesek roda gulir / track pad

@ranneyd Terima kasih atas tanggapan yang begitu cepat. Saya setuju itu adalah batasan. Ini akan berfungsi dengan baik di sebagian besar perangkat jadi bukan masalah besar.

@bvaughn apakah Anda pernah bermain-main dengan position: sticky ? Saya belum melihatnya dalam beberapa saat dan saya tahu dukungan browser kurang, tetapi saya ingin tahu apakah ada cara untuk memanfaatkannya di browser yang didukung ...

Saya memiliki masalah yang sama dengan @ajaymore , yaitu, menyinkronkan dua komponen lamban (seperti yang dinyatakan, ini berfungsi dengan baik pada gulir manual). Saya menguji dengan Chrome di PC, jadi ternyata itu bukan hanya masalah Mac... Adakah yang berhasil mengatasi masalah ini?

@alonrbar Saya memiliki masalah yang sama pada PC Windows.

Saya sebenarnya baru saja menemukan solusi yang cocok untuk saya.
Ide umumnya adalah membuat "kotak bayangan" yang bersembunyi ke kisi asli dan mencuri acara onScroll-nya dan kemudian menggunakannya untuk menggulir kisi asli secara manual serta kisi lain yang diperlukan. Ini sedikit memperlambat kinerja tetapi membuat semua grid tersinkronisasi dengan baik sehingga Anda harus mempertimbangkan trade off.

Kodenya terlihat seperti ini;

import styled from '@emotion/styled';
import * as React from 'react';
import { VariableSizeGrid, VariableSizeGridProps } from 'react-window';
import { SizeUtils } from '../utils';

export interface SyncableGridProps extends VariableSizeGridProps {
    mainGridRef: React.Ref<VariableSizeGrid>;
    shadowGridRef: React.Ref<VariableSizeGrid>;
    hideVerticalScrollbar?: boolean;
}

export class SyncableGrid extends React.PureComponent<SyncableGridProps> {
    public render() {
        const { height, width } = this.props;
        const {
            onScroll,
            mainGridRef: mainGridRef1,
            shadowGridRef: shadowGridRef1,
            ...mainProps
        } = this.props;
        const {
            children,
            style,
            overscanRowsCount,
            overscanColumnsCount,
            overscanCount,
            useIsScrolling,
            onItemsRendered,
            mainGridRef: mainGridRef2,
            shadowGridRef: shadowGridRef2,
            innerRef,
            outerRef,
            ...shadowProps
        } = this.props;
        return (
            <SyncWrapper
                style={{
                    height,
                    width
                }}
            >
                <MainGrid
                    {...mainProps}
                    style={Object.assign({}, style, {
                        overflowY: 'scroll'
                    })}
                    ref={mainGridRef1}
                />
                <GridShadow
                    {...shadowProps}
                    style={{
                        position: 'absolute',
                        top: 0,
                        left: 0
                    }}
                    ref={shadowGridRef1}
                >
                    {() => null}
                </GridShadow>
            </SyncWrapper>
        );
    }
}

// ---------------- //
//      styles      //
// ---------------- //

const SyncWrapper = styled.div`
    position: relative;
    overflow: hidden;
`;

export interface MainGridProps extends VariableSizeGridProps {
    hideVerticalScrollbar?: boolean;
}

export const MainGrid = styled(VariableSizeGrid) <MainGridProps>`
    overflow-y: scroll;
    box-sizing: content-box;
    ${props => {
        if (!props.hideVerticalScrollbar)
            return '';
        const paddingDir = (props.theme.dir === 'rtl' ? 'padding-left' : 'padding-right');
        return `${paddingDir}: ${SizeUtils.scrollbarWidth}px;`;
    }}
`;

export const GridShadow = styled(MainGrid)`
    opacity: 0;
`;

Kemudian di file lain:

<SyncableGrid
    mainGridRef={this.firstGridMain}
    shadowGridRef={this.firstGridShadow}
    onScroll={this.handleFirstGridScroll}
    // other props omitted for bravity...
>
   // children omitted for bravity...
</SyncableGrid>
<SyncableGrid
    mainGridRef={this.secondGridMain}
    shadowGridRef={this.secondGridShadow}
    onScroll={this.handleSecondGridScroll}
    // other props omitted for bravity...
>
   // children omitted for bravity...
</SyncableGrid>

private handleFirstGridScroll = (e: GridOnScrollProps) => {
    const { scrollTop, scrollLeft } = e;

    // synchronize self
    if (this.firstGridMain.current) {
        this.firstGridMain.current.scrollTo({ scrollTop, scrollLeft });
    }

    // synchronize other grid
    if (this.secondGridMain.current) {
        this.secondGridMain.current.scrollTo({ scrollTop, scrollLeft });
        this.secondGridShadow.current.scrollTo({ scrollTop, scrollLeft });
    }
}

@alonrbar ini menarik! Saya akan mencoba ini segera.

Sepertinya grid bayangan hidup di atas grid yang sebenarnya, ya? Jika demikian, acara klik pada "grid nyata" tidak akan berfungsi bukan? Saya kira Anda bisa melakukan sesuatu yang sedikit meretas dengan acara klik + koordinat x/y dan entah bagaimana menerapkannya ke grid utama.

Juga @barbalex re: gagal di windows juga

Ini mungkin bendera chrome sebenarnya. Lihat apakah ada sesuatu di chrome://flags. Microsoft mungkin juga menerapkan hal serupa.

Masalahnya tampaknya terkait dengan pengguliran yang "dihaluskan" lebih cepat daripada bingkai animasi browser. Jika Anda khawatir itu murni masalah kinerja/lag, coba buat implementasi sinkronisasi gulir yang sangat mendasar (di mana pada gulir satu div mengatur posisi gulir yang lain) dan lihat apakah Anda mendapatkan masalah yang sama. Mungkin bahkan melakukannya di vanilla JS murni untuk menghilangkan React sebagai pelakunya

Ahhh @ranneyd Anda benar itu memblokir acara klik ... Perlu memikirkan lebih lanjut tentang itu ...

Saya pikir fitur pengguliran berulir di chrome://flags memiliki pengaruh besar: mematikannya membuat pengguliran dengan roda mouse semulus menggulir dengan bilah gulir untuk saya.

2019-07-15_00h03_42

@alonrbar bagaimana dengan ini:

Tambahkan pengendali gulir ke kisi utama. Di dalamnya Anda melakukan e.preventDefault(); atau sesuatu untuk mencegah pengguliran yang sebenarnya. Kemudian Anda melihat acara untuk mencari tahu berapa banyak yang AKAN digulir, yang dilakukan untuk memindahkan barang-barang lain yang disinkronkan, tetapi kemudian Anda menggunakannya untuk menggulir elemen yang sama secara manual. Jadi, alih-alih menggulir A, lalu menggunakan info itu untuk menggulir B, Anda mencegat gulir di A, membatalkannya, lalu menggunakannya untuk menggulir B dan A itu sendiri. Apakah itu akan berhasil?

Aku tidak dengan komputer untuk menguji. Cukup hacky tapi saya bisa bekerja. @bvaughn pikiran?

@barbalex Anda benar, ini juga berfungsi untuk saya, tetapi saya tidak dapat membuat semua pengguna saya menyalakan dan mematikan bendera chrome

@ranneyd Saya baru saja melihat ke dalamnya dan sedikit mengutak-atik kode sumber ( tautan ) tetapi tampaknya Anda tidak dapat menonaktifkan acara gulir , satu-satunya hal yang dapat Anda lakukan adalah membajak semua roda mouse, acara sentuh dan keyboard yang dapat menyebabkan pengguliran dan mencegah mereka. Tautan (SO) memiliki cuplikan kode pendek untuk itu, tetapi ini membuatnya semakin meretas jadi saya masih memikirkannya ...

@alonrbar ya, meminta pengguna untuk melakukan ini tidak akan berhasil. Anda _dapat_ memberi mereka tautan langsung jika Anda _melakukan_ ingin mencoba: chrome://flags/#disable -threaded-scrolling

@alonrbar eh ini tidak bagus tapi tidak MENGERIKAN jika kita hanya menerapkannya ke grid kita sudah meretas bersama.

Ini hanya roda gulir kan? Apakah tombol panah mewujudkannya juga?

Ini adalah cuplikan dari jawaban yang diterima di sini: https://stackoverflow.com/questions/4770025/how-to-disable-scrolling-temporously

function disableScroll() {
  if (window.addEventListener) // older FF
      window.addEventListener('DOMMouseScroll', preventDefault, false);
  document.addEventListener('wheel', preventDefault, {passive: false}); // Disable scrolling in Chrome
  window.onwheel = preventDefault; // modern standard
  window.onmousewheel = document.onmousewheel = preventDefault; // older browsers, IE
  window.ontouchmove  = preventDefault; // mobile
  document.onkeydown  = preventDefaultForScrollKeys;
}

Seperti yang Anda lihat, ada beberapa kejadian yang perlu Anda tangani dan pertimbangkan kemungkinan konsekuensi dari melakukannya.

Untuk saat ini, karena saya tidak dapat menginvestasikan lebih banyak waktu untuk mencoba menyelesaikan ini sekarang, saya memutuskan untuk menggunakan solusi non-virtual (yang juga tidak sempurna tetapi berfungsi lebih baik untuk kasus penggunaan saya). Anda dapat menemukan kode yang saya gunakan di sini dan di sini (ini adalah bagian dari perpustakaan yang saya tulis yang membungkus react-window ).

@alonrbar ya saya setuju bahwa solusi SO cukup payah.

Saya menemukan solusi Anda, bagaimanapun, sangat menarik. Saya akan melihat apakah saya dapat mengambil celah saya sendiri dan membuatnya bekerja dengan tabel virtual.

@alonrbar jadi saya membuat implementasi non-virtual yang sebenarnya menggunakan pemosisian absolut. Satu-satunya hal yang bergulir adalah wadah luar. Saat gulir, itu memperbarui nilai atas/kiri yang digunakan untuk memposisikan elemen dalam secara absolut. Jadi tidak ada scrollTo atau scrollTop = ... , yang menurut saya membuat saya sangat sedih. Selain itu, bilah gulir SELALU berada di luar SELURUH kisi.

Benda yang saya buat ini dapat secara dinamis memiliki "tajuk beku" di semua sisi. Ini adalah contoh/poc yang SANGAT kasar.

Jelas tidak memiliki virtualisasi yang merupakan masalah serius. Sayangnya saya tidak tahu apakah saya dapat membungkus Grid dari perpustakaan ini di dalamnya, karena lib ini pada dasarnya berjalan pada pengguliran dan ini menghilangkan pengguliran kisi-interior. Tidak yakin apa langkah selanjutnya.

https://codesandbox.io/embed/non-virtual-scroll-synced-table-ot467

Saya pikir logika virtualisasi yang sama yang memicu lib ini dapat diterapkan untuk ini.

@ranneyd implementasi yang sangat keren!

Saya suka komposisi grid dinamis :)

Tetapi yang paling penting, gagasan untuk menggunakan pemosisian absolut alih-alih menggulir mungkin menjadi kunci untuk memecahkan masalah ini. Dengan memperkenalkan div lain, Anda telah memutuskan acara onscroll dari pengguliran konten aktual yang menurut saya membuka kemungkinan baru.

Jika Anda dapat menghubungkan metode react-window 's render Anda mungkin dapat mengganti baris 459 dan seterusnya dengan implementasi Anda. Kemudian Anda akan dapat mengaitkan acara onscroll dan menonaktifkan efeknya saat diperlukan (bilah gulir akan selalu bergerak tetapi Anda dapat mengontrol konten untuk mencegahnya berubah).

@alonrbar jadi saya benar-benar mengimplementasikan kembali algoritma virtualisasi. Ini tidak sebaik tetapi melihat kode sumber ketika Grid ini sampai ke virtualisasi yang sebenarnya pada dasarnya adalah alg yang sama. Namun, performanya sebenarnya lumayan. Saya bisa mendapatkan grid 200x200 tanpa lag, dan 500x500 hanya dengan sedikit. Jika bos saya membiarkan saya mengerjakan ini lagi, saya akan mencoba melakukan implementasi metode render seperti yang Anda sarankan, yang mungkin sebenarnya cukup mudah.

Jika Anda tertarik dengan kisi yang saya buat, saya dapat mempostingnya di suatu tempat. Jika Anda ingin mengambilnya dan menghubungkannya dengan lib ini, jadilah tamu saya

@ranneyd ya saya ingin melihat solusi virtualisasi yang Anda buat. Tidak tahu apakah saya akan menggunakannya tetapi itu pasti akan menarik :)

Juga, saya mencoba menggunakan kode Anda hari ini untuk menerapkan perubahan pada metode render seperti yang saya sarankan, tetapi setelah membaca kembali kode Anda, saya perhatikan bahwa Anda belum benar-benar memutuskan pengguliran seperti yang saya pikir Anda miliki. Yang saya maksud dengan itu adalah bahwa scrollbar dan acara onscroll masih tidak dapat dipisahkan dari scroll konten yang sebenarnya. Saya mengambil ide Anda selangkah lebih maju dan menerapkan gulir yang benar-benar terputus:

https://codesandbox.io/embed/absolute-position-scrolling-1u7vj

Jika Anda melihat kodenya, Anda akan melihat bahwa jika kami menghapus properti top dan left dari komponen Content , konten tidak digulir meskipun bilah gulir lakukan . Saya juga telah memverifikasi bahwa acara mouse kali ini tidak diblokir. 😂
Saya percaya sekarang mungkin untuk menggunakan implementasi ini untuk mengabaikan acara onscroll dan menyinkronkan beberapa kisi secara manual. Tapi itu tentu saja membutuhkan lebih banyak pekerjaan sehingga harus menunggu waktu berikutnya ...

Diskusi ini membuat saya penasaran, jadi saya menyiapkan proyek untuk membandingkan dua jendela reaksi yang disinkronkan-scroll Grid s dengan reaksi-virtualisasi MultiGrid , sehingga saya bisa merasakan bagaimana perbandingan kinerja :
https://github.com/bvaughn/react-window-vs-react-virtualized-synced-grids

Saya mencoba membuat setiap penggunaan semirip mungkin. Beberapa pengamatan awal:

  • react-window terlihat jauh lebih lambat dalam mode DEV tetapi terasa sedikit lebih cepat/lebih responsif dalam pembuatan produksi. (Sulit untuk mengesampingkan bias di pihak saya di sini. Perbedaan dalam produksi lebih kecil.)
  • react-window juga melakukan lebih banyak komit, karena acara gulir pertama-tama memperbarui kisi aktif, lalu kisi pasif melalui pembaruan berjenjang. Saya membuat tambalan ke Grid untuk mendukung melewati pengendali acara gulir "asli" (Bereaksi) (untuk dipanggil dalam pembaruan batch React) daripada onScroll (yang dipanggil selama komit fase). Ini sepertinya perubahan yang cukup menjanjikan ketika saya mengujinya secara lokal, karena ini menghindari render cascading yang terpisah, jadi mungkin saya hanya akan mengubah waktu default onScroll .

@bvaughn Saya mencoba membuat kode efek gulir vanilla yang paling dasar, dengan divA -> onScroll -> setState -> ref.scrollTop dan masih tidak dapat mengatasi hal pengguliran ulir chrome ini. Saya akui tidak menghindari keadaan reaksi dan mengatur ref.scrollTop dalam handler onScroll, tetapi selain itu saya tidak dapat memikirkan cara yang lebih mendasar untuk melakukannya. Jika Anda tidak bisa mendapatkan onScroll -> scrollTop cukup cepat dalam jumlah langkah sesedikit mungkin, bagaimana Anda memperbaikinya? Apakah saya benar-benar kehilangan sesuatu? Sepertinya kisi-kisi tidak perlu bergerak berdasarkan pengguliran (atau pengaturan scrollTop).

Tujuannya adalah agar JavaScript selalu secepat mungkin sehingga mengikuti pengguliran pengelolaan utas.

Saya memang tidak menghindari status reaksi dan menyetel ref.scrollTop dalam penangan onScroll

Untuk memperjelas, ini juga bukan yang dilakukan repo saya. Saya hanya mengatur offset gulir di grid pasif di event handler (terbungkus Bereaksi) yang sama dengan grid aktif, jadi React mengelompokkan pembaruan mereka menjadi satu render+komit. Ini mungkin tidak membuat _banyak_ perbedaan sejujurnya.

@ranneyd dan @bvaughn berbagi dengan Anda pengamatan saya sejauh ini:

  1. Banyak implementasi bekerja dengan cukup baik di browser desktop, tetapi kasus penggunaan utama saya sebenarnya pada perangkat seluler dan di sana saya melihat perbedaan kinerja yang luar biasa.

  2. Menyinkronkan dua kisi non-virtual sederhana berfungsi dengan cukup baik (tidak 100% sempurna tetapi cukup dekat, bahkan di seluler). Ini adalah implementasi saya yang naif, namun berfungsi, dan kasus uji untuk melihatnya beraksi ( yarn storybook ).

  3. Menggunakan gulir "terkendali" (seperti yang saya sarankan dalam komentar ini ) membuat kedua kisi tetap sinkron 100% tetapi cukup lambat di desktop dan terlalu lambat di seluler. Ini celah saya (sangat kasar ...): https://github.com/alonrbar/react-window/commit/c39ce7006dbd590d9c640e37f8a1e78826e4688e

    Meski begitu, saya tergoda untuk melihat apakah strategi "terkontrol" entah bagaimana dapat bekerja untuk menyelesaikan masalah ini pada satu daftar virtual.

  4. Saya berpikir, seperti yang telah Anda berdua sarankan, bahwa mungkin memindahkan _callPropsCallbacks ke _onScroll handler secara langsung dapat membantu mengurangi lag sinkronisasi seminimal mungkin. EDIT - baru saja mencobanya sekarang, tidak terlalu membantu

  5. Dengan cara apa pun Anda mengatasinya, ide bagus IMHO adalah memisahkan logika virtualisasi dari komponen lainnya (mungkin bahkan sebuah kait?) alat peraga komponen. Ini juga akan memungkinkan untuk mengekspor logika ini dan memungkinkan pengguna untuk mengimplementasikan komponen khusus (seperti solusi @ranneyd dalam komentar ini ) berdasarkan logika yang sama.

Pikiran?

@alonrbar solusi saya, yang melakukan pemosisian absolut, jadi tidak ada kisi-kisi yang melakukan pengguliran, bekerja dengan sangat baik hingga sekitar 300x300 di mana ia menjadi agak lamban (perhatikan itu tetap sinkron 100% dari waktu, pengguliran saja menjadi sedikit lamban). Pada ukuran yang lebih besar, saya pikir itu hanya memproses/memetakan array besar. Saya pikir ada beberapa pengoptimalan
Saya bisa melakukannya, dan saya tidak sepenuhnya yakin itu ada hubungannya dengan sinkronisasi gulir sebanyak implementasi virtualisasi saya yang cukup sederhana. Saya dapat melakukan lebih banyak caching, misalnya, dan saya dapat menghitung apakah sebuah sel harus divirtualisasikan atau tidak lebih awal untuk menghindari pemanggilan fungsi render sama sekali (dan kemudian cache yang lebih baik mungkin).

Saya belum menguji apa pun di ponsel. Saya akan memberikan beberapa kode untuk Anda coba sebentar lagi.

Saya benar-benar ingin melihat tes @bvaughn . Dia mengatakan menghubungkan langsung ke gulungan asli memperbaikinya, Anda mengatakan tidak. Saya ingin melihat sendiri.

Sejauh menarik logika virtualisasi ke dalam kait atau fungsi independen, itu menjadi sangat rumit karena logika secara intrinsik terhubung ke tampilan. Sepertinya juga banyak tweak kinerja melibatkan hal-hal caching dan memoisasi yang mungkin sulit untuk diringkas dalam satu kait atau fungsi, atau setidaknya sampai pada tingkat di mana ia mendapatkan kinerja yang sama. Tapi saya akan melihat apa yang saya miliki dan melihat seberapa banyak logika yang bisa saya tarik.

PS:
Satu hal yang baru saja saya pikirkan yang mungkin tidak akan berhasil adalah melakukan transisi seperti hal yang mencela + css. Jika kita hanya menjalankan satu acara gulir setiap 100 ms atau sesuatu dan menganimasikan gerakan itu mungkin terlihat lebih baik. Mungkin juga tampak kurang responsif secara substansial. Ini seperti bagaimana mereka melakukan World of Warcraft (jika ada lag atau latensi tinggi, mereka akan membuat karakter bergerak dalam garis lurus dan kemudian memperbaikinya setelah mereka mendapat info ke mana mereka sebenarnya pergi).

Saya benar-benar ingin melihat tes @bvaughn . Dia mengatakan menghubungkan langsung ke gulungan asli memperbaikinya, Anda mengatakan tidak. Saya ingin melihat sendiri.

Saya tidak mengatakan itu :smile: Saya hanya mengatakan itu menghindari render tambahan dan mutasi DOM yang tidak perlu. Seberapa besar pengaruhnya terhadap kinerja aktual tidak jelas bagi saya. Itu memang tampak seperti perubahan positif secara keseluruhan, terlepas dari itu.

@alonrbar @bvaughn Saya harus benar-benar mendokumentasikan kode saya, tetapi inilah versi terbaru:

https://github.com/ranneyd/synced-table

@alonrbar @bvaughn jadi inilah hal menyenangkan yang baru saya temukan:

Solusi saya TIDAK berfungsi di Chrome 75 pada layar macbook. Ketika rekan kerja saya belum memperbarui chrome, itu berhasil. Jika mereka menggunakan monitor eksternal, itu berfungsi. Di layar laptop mereka itu tertinggal.
😩

Hmm...ada beberapa perbedaan dengan monitor eksternal, dengan refresh rate atau scaling. Ketika Anda mengatakan itu "tidak berhasil" apa maksud Anda, khususnya?

Salahku. Maksud saya pengguliran tidak lagi disinkronkan. Jika Anda mengkloning repo saya dan menjalankannya, Anda dapat membandingkan monitor eksternal vs layar laptop. Di monitor mereka sangat sinkron. Di layar laptop, header berkedip (jangan perbarui dengan kecepatan yang sama dengan elemen pengguliran).

Saya sebenarnya sudah menyerah dan saya mencobanya dengan position: sticky . Ini benar-benar bekerja. Dukungan browser tidak 100%, tetapi sebenarnya jauh lebih baik daripada setahun yang lalu.

Ada lib ini yang merupakan polyfill non-polyfill yang mungkin berfungsi, tetapi browser yang kami targetkan mendukung fitur tersebut.
https://github.com/dollarshaveclub/stickybits

@alonrbar @bvaughn di sini adalah versi terbaru. Ini menggunakan position: sticky . Saya jelaskan sedikit di readme. Kode masih perlu didokumentasikan.

https://github.com/ranneyd/sticky-table

Solusi saya TIDAK berfungsi di Chrome 75 pada layar macbook. Ketika rekan kerja saya belum memperbarui chrome, itu berhasil. Jika mereka menggunakan monitor eksternal, itu berfungsi. Di layar laptop mereka itu tertinggal.
😩

@ranneyd Terpikir oleh saya hari ini bahwa ini mungkin terkait dengan frame rate. Kecepatan bingkai untuk monitor eksternal mungkin misalnya 30 fps, dalam hal ini menurut saya browser mungkin mengirimkan lebih sedikit peristiwa "gulir" (karena menurut saya sebagian besar browser hanya mengirimkan satu per bingkai/cat). Mungkin ini sebabnya lag lebih terlihat untuk Anda di monitor eksternal?

@bvaughn saya pikir harus begitu. OS MENGATAKAN itu mengirim 60hz dan saya pikir spesifikasi monitor mengatakan 60hz, tetapi tidak akan mengejutkan saya jika seseorang berbohong 😂

@ranneyd Saya minta maaf untuk mengatakan bahwa saya tidak terlalu tersedia saat ini jadi saya tidak dapat melihat dengan tepat solusi Anda, tetapi dari pandangan sekilas saya perhatikan, itu terlihat cukup rapi dan Anda telah membuat useVirtual menghubungkan dan memisahkan logika secara elegan dari rendering.
Saya pikir akan lebih bagus jika Anda entah bagaimana bisa membuat permintaan tarik untuk react-window , menggunakannya di sana dan mungkin mengekspos metode renderTable mana Anda bisa memasukkan logika render Anda. Dengan cara ini perpustakaan Anda bisa membungkus jendela reaksi alih-alih menggantinya. Saya percaya akan lebih disukai jika kita dapat memanfaatkan pendekatan ini dan menyelesaikan masalah di dalam react-window yang sudah cukup banyak diuji pertempuran dan tersebar luas.

Pada masalah lain, saya berpikir bahwa jika scrollTo akan menggunakan manipulasi DOM langsung (mungkin selain mengatur status tetapi dalam hal apa pun sebelum melakukannya) maka hasil sinkronisasi dua tabel akan menjadi lebih lancar. Jika saya tidak salah @bvaughn juga menyarankan sesuatu ke arah ini.

@ranneyd solusi sticky-table Anda terlihat sangat bagus dan saya akan sangat senang dapat menggunakannya. Apakah Anda mempertimbangkan untuk merilis api yang dapat digunakan untuk itu pada npm ?

@bvaughn @ranneyd

Jadi, setelah beberapa waktu saya baru-baru ini kembali ke masalah ini. Menggunakan kode dan ide dari perpustakaan Anda serta dari recyclerlistview dan react-virtual-grid Saya akhirnya dapat mencapai perilaku yang saya inginkan, yaitu, kisi kinerja tinggi dengan baris dan kolom tetap yang berfungsi baik di desktop dan seluler perangkat (pengguliran halus tanpa sel kosong/putih).

TLDR itu adalah bahwa saya menggunakan daur ulang bersama-sama dengan posisi lengket dan bagian yang paling menarik dari kode dapat ditemukan di sini dalam metode ini .

Penghargaan dan lebih banyak lagi motivasi untuk menulis solusi lain ada di sini . Terima kasih!

Saya akan meninggalkan tautan ke contoh Kode Sandbox yang berfungsi di sini jika ada orang lain yang menemukan masalah ini di masa mendatang.
https://codesandbox.io/s/y3pyp85zm1

Sangat bagus, sampai Anda menggulir sepenuhnya ke kanan: maka tajuk tidak sejajar dengan konten (dengan lebar bilah gulir vertikal gulir).
Apakah Anda kebetulan menemukan solusi untuk itu?
Terima kasih

Saya akan meninggalkan tautan ke contoh Kode Sandbox yang berfungsi di sini jika ada orang lain yang menemukan masalah ini di masa mendatang.
https://codesandbox.io/s/y3pyp85zm1

Sangat bagus, sampai Anda menggulir sepenuhnya ke kanan: maka tajuk tidak sejajar dengan konten (dengan lebar bilah gulir vertikal gulir).
Apakah Anda kebetulan menemukan solusi untuk itu?
Terima kasih

Pengungkapan penuh: Saya sebenarnya telah membuat versi saya sendiri dari awal. Ini memiliki virtualisasi sendiri yang sangat didasarkan pada lib ini. Alasan saya melakukan ini adalah karena ada masalah pada MacBook dan dengan bendera Chrome tertentu di mana animasi untuk pengguliran terjadi dengan waktu yang berbeda dari JS. Menggunakan ScrollSync membutuhkan terlalu banyak panggilan fungsi yang diteruskan dan terlalu lambat. Saya pada dasarnya harus membuat ulang perpustakaan dengan header lengket pada intinya (saya tidak menggunakan position: sticky seperti yang saya katakan sebelumnya di utas ini. Saya perlu mengunggah apa yang saya miliki sekarang di beberapa titik).

Yang mengatakan, cara saya mengatasi masalah ini adalah dengan overflow: scroll untuk memaksa scrollbar dan kemudian menambahkan padding itu ke sel terakhir, atau saya secara dinamis menentukan apakah ada scrollbar (dan berapa lebarnya) menggunakan a ref dan meletakkan div tak terlihat di dalamnya, mengukur ukuran bilah gulir, dan menghapus simpul DOM.

Saya telah menggunakan overflow-y: overlay yang membuat hamparan bilah gulir. Sayangnya ini hanya berfungsi untuk browser webkit.

Oke terima kasih atas infonya. Tampaknya header yang lengket adalah kebutuhan yang umum, sehingga hanya menambahkan opsi baris pertama yang lengket ke jendela reaksi akan menjadikannya pengganti reaksi-virtualisasi dalam banyak kasus lainnya. Tidakkah menurutmu begitu?

@ranneyd terima kasih lagi, saya mengikuti pendekatan Anda dan sinkronisasi gulir berfungsi dengan baik.

Luapan tersembunyi pada bilah gulir menyebabkan ketidaksejajaran di dekat ujung kisi:

gridalignement

Adakah saran tentang cara memperbaikinya?

Contoh CodeSandbox

Terima kasih sebelumnya

Terlambat ke pesta, tetapi jika Anda hanya menambahkan satu baris ke leftRef dan menyembunyikan overflow, pada dasarnya Anda menyelesaikan pb. (Saya melakukannya juga tidak harus menyinkronkan leftRef -> Main Grid

  const headerRef = React.useRef();
  const leftRef   = React.useRef();

  return <Box classes={{
            root:classes.tableContainer
          }}>
      <AutoSizer>
        {({ height, width }) => (<>
        {/*---------------- LA TABLE -------------*/}
          <Grid
            columnCount={1000}
            columnWidth={100}
            height={height}
            rowCount={1000}
            rowHeight={35}
            width={width}
            onScroll={({ scrollLeft, scrollTop }) => {
              if (leftRef.current) {
                leftRef.current.scrollTo({ scrollTop });
              }
              if (headerRef.current) {
                headerRef.current.scrollTo({ scrollLeft });
              }
            }}
          >
            {({ columnIndex, rowIndex, style }) => (
              <Box style={style} classes={{root:classes.cell}}>
                Item {rowIndex},{columnIndex}
              </Box>
            )}
          </Grid>
          {/*---------------- HEADER -------------*/}
          <Grid
            ref={headerRef}
            outerElementType={React.forwardRef((props, ref) => (
              <div ref={ref}  {...props} style={{...props.style,position:"absolute",overflow:"hidden",top:0,right:0,left:150}} />
            ))}
            columnCount={1001}  /*columns count +1 for scroll problems*/
            columnWidth={100}
            height={60}
            rowCount={1}
            rowHeight={60}
            width={width}
          >
            {({ columnIndex, rowIndex, style }) => (
              <Box style={style} classes={{root:classes.headerCell}}>
                Header {rowIndex},{columnIndex}
              </Box>
            )}
          </Grid>  
          {/*---------------- LEFT COL -------------*/}
          <Grid
            ref={leftRef}
            outerElementType={React.forwardRef((props, ref) => (
              <div ref={ref}  {...props} style={{...props.style,position:"absolute",overflow:"hidden",top:60,left:0}} />
            ))}
            columnCount={1}
            columnWidth={150}
            height={height}
            rowCount={251} /** add 1 for scroll problems at the end */
            rowHeight={140}
            width={150}
          >
            {({ columnIndex, rowIndex, style }) => (
              <Box style={style} classes={{root:classes.headerCell}}>
                Left {rowIndex},{columnIndex}
              </Box>
            )}
          </Grid>  
        </>)}
      </AutoSizer>
    </Box>
Apakah halaman ini membantu?
0 / 5 - 0 peringkat