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

React Native中使用useSelector时如何保持ScrollView的滚动位置

问题根因
  1. useSelector 本身会订阅指定的Redux状态,只要对应的状态发生变更,就会触发当前组件重新渲染,重渲染会让ScrollView重置到初始的顶部位置,打断首次scrollTo的执行逻辑。
  2. 把useSelector返回值直接作为useState初始值的写法本身不合理:useState初始值仅在组件首次挂载时生效,后续Redux状态更新时,局部state不会自动同步,但useSelector仍然会触发重渲染,相当于多了一次无意义的重渲染。
  3. 该写法会导致每次组件渲染时useSelector都会被执行,哪怕它的返回值已经不再用于更新局部state,额外增加了重渲染的触发概率。
    你改成空数组作为初始值后滚动正常,大概率是你同步删除了组件内的useSelector调用,没有了Redux状态订阅带来的额外重渲染,自然不会出现滚动位置被重置的问题。
修复方案

根据你的业务场景可以选择以下任意一种方案:

  • 方案1:拆分ScrollView为独立纯组件

    把ScrollView和内部渲染逻辑抽成单独的组件,用React.memo包裹,仅将渲染必需的参数作为props传入。父组件订阅Redux状态重渲染时,只要传给ScrollView的props没有变化,ScrollView就不会重渲染,滚动位置自然不会被重置。
    示例代码:

    // 抽离的独立ScrollView组件
    const ContentScrollView = React.memo(({ data1, data2, initScrollY }) => {
      const scrollRef = useRef(null);
    
      useEffect(() => {
        scrollRef.current?.scrollTo({ y: initScrollY, animated: false });
      }, [initScrollY]);
    
      return (
        <ScrollView ref={scrollRef}>
          {/* 内部渲染data1、data2的业务逻辑 */}
        </ScrollView>
      )
    })
    
    // 父组件
    const Page = () => {
      const reduxData1 = useSelector(state => state.data1);
      const reduxData2 = useSelector(state => state.data2);
      const [data1, setData1] = useState(reduxData1);
      const [data2, setData2] = useState(reduxData2);
    
      // 如果需要同步Redux数据到局部state,单独监听变更
      useEffect(() => setData1(reduxData1), [reduxData1]);
      useEffect(() => setData2(reduxData2), [reduxData2]);
    
      return <ContentScrollView data1={data1} data2={data2} initScrollY={100} />
    }
    
  • 方案2:取消useSelector订阅,仅取Redux初始值

    如果你不需要实时监听Redux中data1、data2的变化,只是需要拿初始值赋值给局部state,可以直接调用Redux的getState()方法取值,不要用useSelector,避免额外的状态订阅带来的重渲染。
    示例代码:

    import store from '你的redux store文件路径';
    
    // 组件内直接取初始值
    const [data1, setData1] = useState(store.getState().data1);
    const [data2, setData2] = useState(store.getState().data2);
    
  • 方案3:缓存滚动位置,重渲染后自动恢复

    如果业务上必须让组件跟着Redux状态更新重渲染,可以监听ScrollView的滚动事件实时缓存位置,每次组件重渲染完成后自动滚动到缓存的位置,规避重渲染带来的位置重置问题。
    示例代码:

    const scrollRef = useRef(null);
    // 用ref缓存滚动位置,重渲染不会重置ref的值
    const scrollOffsetRef = useRef(0);
    
    const handleScroll = useCallback((e) => {
      scrollOffsetRef.current = e.nativeEvent.contentOffset.y;
    }, []);
    
    // 每次渲染完成后恢复滚动位置
    useEffect(() => {
      scrollRef.current?.scrollTo({ y: scrollOffsetRef.current, animated: false });
    });
    
    return (
      <ScrollView 
        ref={scrollRef} 
        onScroll={handleScroll}
        scrollEventThrottle={16}
      >
        {/* 业务渲染逻辑 */}
      </ScrollView>
    )
    

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.10.07 13:51:02