You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

基于NgRx实现多组件滚动同步的方案咨询

用NgRx实现两个组件滚动同步的完整方案

1. 修正Reducer逻辑

你当前的Reducer没有更新状态,需要修改为接收动作传入的data并返回新状态(保持NgRx状态不可变性):

export const initialState: Readonly<ListStateModel> = {displayedElemList: [], scrollY: 0};

export const scrollReducer = createReducer(
  initialState,
  on(getScrollAction, (_state, { data }) => {
    return {
      ..._state,
      scrollY: data.scrollY,
      displayedElemList: data.displayedElemList
    };
  })
);

2. 创建Selector

通过Selector封装状态查询逻辑,让组件可以精准订阅所需数据:

import { createFeatureSelector, createSelector } from '@ngrx/store';

// 获取整个列表状态切片
export const selectListState = createFeatureSelector<ListStateModel>('list');

// 派生滚动位置的Selector
export const selectScrollY = createSelector(
  selectListState,
  (state) => state.scrollY
);

// 派生显示元素列表的Selector
export const selectDisplayedElemList = createSelector(
  selectListState,
  (state) => state.displayedElemList
);

3. 滚动发起组件实现

监听滚动事件,将滚动位置和列表数据dispatch到Store:

模板(scroll-source.component.html)

<div class="scroll-container" (scroll)="onScroll($event)">
  <div *ngFor="let item of displayedList">{{ item }}</div>
</div>

组件类(scroll-source.component.ts)

import { Component } from '@angular/core';
import { Store } from '@ngrx/store';
import { getScrollAction } from './path-to-actions';
import { ListStateModel } from './path-to-model';

@Component({
  selector: 'app-scroll-source',
  templateUrl: './scroll-source.component.html',
  styleUrls: ['./scroll-source.component.css']
})
export class ScrollSourceComponent {
  displayedList: any[] = []; // 替换为你的实际列表数据

  constructor(private store: Store) {}

  onScroll(event: Event) {
    const target = event.target as HTMLElement;
    const scrollData: ListStateModel = {
      scrollY: target.scrollTop,
      displayedElemList: this.displayedList
    };
    this.store.dispatch(getScrollAction({ data: scrollData }));
  }
}

4. 滚动同步组件实现

订阅Store中的滚动位置,同步自身容器的滚动状态:

模板(scroll-target.component.html)

<div class="scroll-container" #scrollContainer>
  <div *ngFor="let item of displayedList$ | async">{{ item }}</div>
</div>

组件类(scroll-target.component.ts)

import { Component, ElementRef, ViewChild, OnInit } from '@angular/core';
import { Store } from '@ngrx/store';
import { selectScrollY, selectDisplayedElemList } from './path-to-selectors';
import { Observable } from 'rxjs';

@Component({
  selector: 'app-scroll-target',
  templateUrl: './scroll-target.component.html',
  styleUrls: ['./scroll-target.component.css']
})
export class ScrollTargetComponent implements OnInit {
  @ViewChild('scrollContainer') scrollContainer!: ElementRef<HTMLElement>;
  scrollY$: Observable<number>;
  displayedList$: Observable<any[]>;

  constructor(private store: Store) {
    this.scrollY$ = this.store.select(selectScrollY);
    this.displayedList$ = this.store.select(selectDisplayedElemList);
  }

  ngOnInit() {
    this.scrollY$.subscribe(scrollY => {
      this.scrollContainer.nativeElement.scrollTop = scrollY;
    });
  }
}

5. 关键注意事项

  • 确保两个组件的滚动容器样式(高度、overflow等)完全一致,避免同步偏差。
  • 若滚动频繁,可添加debounceTime减少动作触发频率:
    import { Subject } from 'rxjs';
    import { debounceTime } from 'rxjs/operators';
    
    private scrollSubject = new Subject<number>();
    
    ngOnInit() {
      this.scrollSubject.pipe(debounceTime(30)).subscribe(scrollY => {
        const scrollData: ListStateModel = {
          scrollY,
          displayedElemList: this.displayedList
        };
        this.store.dispatch(getScrollAction({ data: scrollData }));
      });
    }
    
    onScroll(event: Event) {
      const target = event.target as HTMLElement;
      this.scrollSubject.next(target.scrollTop);
    }
    
  • 在模块中注册Reducer:
    import { StoreModule } from '@ngrx/store';
    import { scrollReducer } from './path-to-reducer';
    
    @NgModule({
      imports: [
        // 根模块用forRoot,feature模块用forFeature
        StoreModule.forRoot({ list: scrollReducer })
      ]
    })
    export class AppModule {}
    

内容的提问来源于stack exchange,提问作者Lelovias

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.01 18:40:26