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

React Native中无法将排序后的数组传递给FlatList,如何解决?

问题描述

从Firebase Datastore拉取user.notes数据,user是文档,notes是存储对象的数组,每个对象包含时间值,示例结构:

user = [..., {title: 'First', details: 'first one', time: '1677959929938'}, ...]

将状态变量user赋值给普通数组arr,调用changeOrder()函数时希望完成排序,但遇到以下问题:

  • 选择升序(timeA)时,日志输出正确顺序,但闪显后迅速恢复原序
  • 选择降序(timeD)时显示正确,但本身就是原始顺序
  • FlatList无法正确接收排序后的arr数组

用户组件代码:

const Notes = ({navigation}) => {
    const [loading, setLoading] = useState(true);
    const [value, setValue] = useState('');
    const [user, setUser] = useState([]);
    
    let arr = user.notes;

    function changeOrder(o) {
      setValue(o);
      arr = user.notes;
      switch (value) {
        case 'titleA':
          console.log('titleA');
          break;
        case 'titleD':
          console.log('titleD');
          break;
        case 'timeA':
          arr.sort((a,b)=> a.time - b.time);
          console.log('Artan: ', arr);
          break;
        case 'timeD':
          arr.sort((a,b)=> b.time - a.time);
          console.log('Azalan: ', arr);
          break;
  
        default:
          console.log('empty');
      }
    }

    const q = query(collUser, where("email", "==", auth.currentUser.email))

    useEffect(()=>{
      const subscriber = onSnapshot(q, (snapshot) => {
        snapshot.docs.forEach(doc => {
          setUser({...doc.data(), id: doc.id});
        });
        
        setLoading(false);
      });
      return () => subscriber();
    },[user])
    
    if (loading) {
        return <ActivityIndicator style={{flex:1, justifyContent: 'center', alignItems: 'center'}} size="large" color="#0000ff" />;
    }

    function addHandler () {
      navigation.navigate('AddNote', {docId: user.id, notes: user.notes});
    }

    function renderTile({item}) {
        function pressHandler() {navigation.navigate("ViewNote", {noteId: item.id, t: item.title, d: item.details, time: item.createdAt});}
        // console.log(item.title);
        return <NoteTile 
                title={item.title}
                det={item.details}
                pressFunc={pressHandler} />;
    }

  return (
      <View style={styles.flatlistContainer}>

        <View style={styles.headerContainer}>
          <Text style={styles.header}>Welcome</Text>
          <View style={styles.pickerContainer}>
            <Picker changeOrder={changeOrder} />
          </View>
        </View>

        <FlatList data={arr}
          keyExtractor={(item) => item.time}
          renderItem={renderTile}
          numColumns={2}
          style={styles.flatlist} />
        
        <View style={styles.buttonContainer}>
          <Pressable android_ripple={{color: '#d9d9d9'}} onPress={addHandler}>
            <MaterialIcons style={styles.icon} name='add' size={40}/>
          </Pressable>
        </View>
      </View>
  )
}

export default Notes
问题原因与修复方案

核心问题

  1. 原地排序污染原状态:Array.sort()是原地排序方法,会直接修改user.notes原数组,后续Firebase快照更新时会重置数据,导致排序后恢复原序。
  2. 异步状态更新逻辑错误:setValue(o)是异步操作,switch中直接使用value拿到的是旧值,无法触发正确的排序分支。
  3. 普通变量无法触发重渲染:arr是普通变量,修改后不会触发组件重新渲染,FlatList无法感知数据变化。

修复后的代码

import { useMemo } from 'react';

const Notes = ({navigation}) => {
    const [loading, setLoading] = useState(true);
    const [sortType, setSortType] = useState('');
    const [user, setUser] = useState({ notes: [] }); // 初始设为对象,避免undefined

    // 用useMemo缓存排序结果,依赖变化时才重新计算
    const sortedNotes = useMemo(() => {
        if (!user.notes) return [];
        // 复制原数组,避免修改原状态
        const notesCopy = [...user.notes];
        
        switch (sortType) {
            case 'titleA':
                return notesCopy.sort((a, b) => a.title.localeCompare(b.title));
            case 'titleD':
                return notesCopy.sort((a, b) => b.title.localeCompare(a.title));
            case 'timeA':
                return notesCopy.sort((a, b) => Number(a.time) - Number(b.time));
            case 'timeD':
                return notesCopy.sort((a, b) => Number(b.time) - Number(a.time));
            default:
                return notesCopy;
        }
    }, [user.notes, sortType]);

    const q = query(collUser, where("email", "==", auth.currentUser.email))

    useEffect(()=>{
        const subscriber = onSnapshot(q, (snapshot) => {
            snapshot.docs.forEach(doc => {
                setUser({...doc.data(), id: doc.id});
            });
            setLoading(false);
        });
        return () => subscriber();
    }, []); // 移除user依赖,避免无限循环

    if (loading) {
        return <ActivityIndicator style={{flex:1, justifyContent: 'center', alignItems: 'center'}} size="large" color="#0000ff" />;
    }

    function addHandler () {
        navigation.navigate('AddNote', {docId: user.id, notes: user.notes});
    }

    function renderTile({item}) {
        function pressHandler() {
            navigation.navigate("ViewNote", {noteId: item.id, t: item.title, d: item.details, time: item.createdAt});
        }
        return <NoteTile 
                title={item.title}
                det={item.details}
                pressFunc={pressHandler} />;
    }

    return (
        <View style={styles.flatlistContainer}>
            <View style={styles.headerContainer}>
                <Text style={styles.header}>Welcome</Text>
                <View style={styles.pickerContainer}>
                    <Picker changeOrder={(o) => setSortType(o)} />
                </View>
            </View>

            <FlatList data={sortedNotes}
                keyExtractor={(item) => item.time}
                renderItem={renderTile}
                numColumns={2}
                style={styles.flatlist} />
            
            <View style={styles.buttonContainer}>
                <Pressable android_ripple={{color: '#d9d9d9'}} onPress={addHandler}>
                    <MaterialIcons style={styles.icon} name='add' size={40}/>
                </Pressable>
            </View>
        </View>
    )
}

export default Notes

关键修复点

  • 避免修改原状态:每次排序前复制user.notes为新数组,防止污染原始状态数据。
  • 状态变量控制排序:用sortType状态记录当前排序方式,结合useMemo缓存排序结果,确保状态变化时触发组件重渲染。
  • 修复无限循环:移除useEffect依赖中的user,避免因setUser更新状态导致重复触发快照监听。
  • 同步处理排序逻辑:直接在Picker回调中设置sortType,避免依赖异步更新后的旧值。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.29 04:30:46