Список не видит обновленное состояние массива - PullRequest
0 голосов
/ 06 февраля 2020

Ожидаемое поведение : два рендера. Первоначально вы видите загрузчик, и когда студенты успешно выбираются в фоновом режиме, загрузчик изменяется на список, который заполнен студентами.

Что я пропускаю?

Начальный рендер

  1. использовать состояние, чтобы установить elem для отображения (<Loader/>) и массив students (один начальный объект для тесты)

  2. выборка students массив объектов в useEffect

  3. при успешном обновлении состояния выборки - установите students в выбранный массив и от elem до renderList, которые будут заполнять элементы списка с students содержимым

  4. console.log(students.length) показывает 1 в этот момент - правильно

  5. состояние обновлено, поэтому повторное отображение

Второй рендер

console.log(students.length) показывает 49 - students правильно обновлено

ПОЧЕМУ? - 7. renderList видит начальное значение students (1 объект)

Это потому, что отображаемый elem и его содержимое (students) находятся в состоянии, и они оба обновляются в useEffect одновременно?

<!-- language: lang-js -->

all imports

export default function AttendanceSingleClass(props) {

    const {classId, groupId} = props.route.params.classObj;
    const [students, _setStudents] = useState([{sId:1,name:'foo',fname:'bar'}]);
    const [elem, _setElem] = useState(<Loader/>);

    useEffect(() => {

            async function _getStudentsList() {
                try {
                    const students = await api.getStudentsList(groupId, classId);
                    _setStudents(students);
                    _setElem(renderList)
                } catch (e) {
                    console.log(e)
                }
            }
            _getStudentsList();
        },[classId]);

    const ListEl = (props) => {
        return (
            <View>..contents of list item...</View>
        )
    }

    const renderList = () => {
        return (
            <View>
                <FlatList
                    data={students}
                    extraData={students}
                    keyExtractor={item => item.sId}
                    renderItem={({item}) => <ListEl item={item} />}
                />
            </View>
        );
    }

    console.log(students.length);

    return elem;
}

после некоторых экспериментов Я попытался передать массив students как prop в renderList - работает. Но все же я не понимаю, почему он видит students в глобальной области видимости, но только в начальном значении, а не в обновленном состоянии (как показано выше).

<!-- language: lang-js -->

export default function AttendanceSingleClass(props) {

    (...)
    useEffect(() => {

            async function _getStudentsList() {
                try {
                     _setElem(renderList(students))
                } 
            }
            _getStudentsList();
        }, [classId]);

    const renderList = (students) => {
        return (...);
    }

    (...);

}

1 Ответ

1 голос
/ 06 февраля 2020

Я бы предложил выбрать другой подход. Кажется плохой практикой хранить компонент или функцию рендеринга в состоянии, в то время как уже зная, что будет визуализировано, когда.

Например, мне нравится использовать состояние loading, например:

export default function AttendanceSingleClass(props) {
  const { classId, groupId } = props.route.params.classObj;
  const [students, _setStudents] = useState([{ sId: 1, name: 'foo', fname: 'bar' }]);
  const [loading, _setLoading] = useState(true);

  useEffect(() => {
    _setLoading(true);

    async function _getStudentsList() {
      try {
        const students = await api.getStudentsList(groupId, classId);
        _setStudents(students);
        _setLoading(false);
      } catch (e) {
        console.log(e);
      }
    }

    _getStudentsList();
  }, [classId]);

  const ListEl = (props) => {
    return (
      <View>..contents of list item...</View>
    );
  };

  // early return the <Loader /> component when loading is true
  if (loading) {
    return <Loader />;
  }

  return (
    <View>
      <FlatList
        data={students}
        extraData={students}
        keyExtractor={item => item.sId}
        renderItem={({ item }) => <ListEl item={item} />}
      />
    </View>
  );
}
...