【问题标题】:State doesn't update on React Native状态不会在 React Native 上更新
【发布时间】:2020-06-02 20:06:57
【问题描述】:

我正在做我的 react native 应用的通知页面。它具有无限滚动和“拉动刷新”选项。进入它工作的页面,它也可以拉动刷新。 当我向下滚动时会出现问题,因为它似乎调用服务器来获取新通知,但它没有连接到数组。

import React, { useState, useEffect, useCallback, Component } from "react";
import {
  View,
  Text,
  FlatList,
  Button,
  Platform,
  ActivityIndicator,
  StyleSheet,
  ScrollView,
  RefreshControl,
  SafeAreaView,
} from "react-native";
import { useSelector, useDispatch } from "react-redux";
import i18n from "i18n-js";
import Colors from "../../constants/Colors";
import { getNotificationList } from "../../utils/NotificationsUtils";
import Card from "../../components/UI/Card";

const NotificationsScreen = (props) => {
  const [refreshing, setRefreshing] = useState(false);
  const [isLoading, setIsLoading] = useState(false);
  const [page, setPage] = useState(0);
  const [notifications, setNotifications] = useState([]);
  const [error, setError] = useState();

  const dispatch = useDispatch();

  const onRefresh = useCallback(async () => {
    setRefreshing(true);
    setNotifications([]);
    setPage(0);

    console.log("-- Refreshing --");

    getNotifications().then(() => {
      setRefreshing(false);
    });
  }, [dispatch, setRefreshing]);

  const fetchMoreNotifications = useCallback(async () => {
    const newPage = page + 7;
    setPage(newPage);
    console.log(
      "FETCH MORE from page " + newPage + " on array of " + notifications.length
    );

    getNotifications().then(() => {
      setIsLoading(false);
    });
  }, [dispatch, getNotifications]);

  const getNotifications = useCallback(async () => {
    setError(null);
    setIsLoading(true);
    try {
      console.log("Get from page " + page);
      // let fromRecord = (page - 1) * 7;
      const retrievedNotifications = await getNotificationList(
        page,
        7,
        true,
        false
      );
      console.log(
        "Setting " +
          retrievedNotifications.response.notifications.length +
          " new notifications on an already existing array of " +
          notifications.length +
          " elements"
      );

      let updatedNews = notifications.concat(
        retrievedNotifications &&
          retrievedNotifications.response &&
          retrievedNotifications.response.notifications
      );
      setNotifications(updatedNews);
    } catch (err) {
      setError(err.message);
    }
    setIsLoading(false);
  }, [dispatch, setIsLoading, setNotifications, setError]);

  useEffect(() => {
    setIsLoading(true);
    getNotifications(page).then(() => {
      setIsLoading(false);
    });
  }, [dispatch, getNotifications]);

  return (
    <View>
      {error ? (
        <View style={styles.centered}>
          <Text>Error</Text>
        </View>
      ) : refreshing ? (
        <View style={styles.centered}>
          <ActivityIndicator size="large" color={Colors.primary} />
        </View>
      ) : !notifications || !notifications.length ? (
        <View style={styles.centered}>
          <Text>No data found</Text>
        </View>
      ) : (
        <FlatList
          refreshControl={
            <RefreshControl refreshing={refreshing} onRefresh={onRefresh} />
          }
          data={notifications}
          keyExtractor={(notification) => notification.notificationQueueId}
          onEndReached={fetchMoreNotifications}
          onEndReachedThreshold={0.5}
          initialNumToRender={4}
          renderItem={(itemData) => (
            <View
              style={{
                marginTop: 10,
                height: 150,
                width: "100%",
              }}
            >
              <Card style={{ height: 150, backgroundColor: "white" }}>
                <Text style={{ fontSize: 16, color: Colors.black }}>
                  {itemData.item.text}
                </Text>
              </Card>
            </View>
          )}
        />
      )}
    </View>
  );
};

const styles = StyleSheet.create({
  centered: {
    flex: 1,
    justifyContent: "center",
    alignItems: "center",
  },
});

export default NotificationsScreen;

如果我滚动到结束它会触发“fetchMoreNotifications”功能,我会在控制台中得到这个:

FETCH MORE from page 7 on an array of 0
Get from page 0
Setting 7 new notifications on an already existing array of 0 elements
FETCH MORE from page 7 on an array of 0
Get from page 0
Setting 7 new notifications on an already existing array of 0 elements
FETCH MORE from page 7 on an array of 0
Get from page 0
Setting 7 new notifications on an already existing array of 0 elements
...and so on

如您所见,即使我之前保存了通知,它也会显示“现有 0 个元素的数组”。可能useCallback的依赖有问题?

【问题讨论】:

  • 你的reducer在哪里你试图连接你的数组?
  • 我看到您在这里收到通知,getNotificationList 确定您在第二次尝试时收到数据吗?
  • 嗨 Waheed,你给了我一个很好的建议。确实 getNotificationList 工作正常,但它总是检索通知集 0-7,因此 FlatList 不显示新通知(因为它们具有相同的 id)。再次调试代码,我看到页面有一个奇怪的行为(我猜这是真正的问题)。确实,当“fetchMoreNotifications”被触发时,“newPage”值是正确的(newPage + 7),但在“getNotifications”内部,“page”值是0,怎么可能呢?
  • 嗨 panagulis,这个问题需要调试,我尝试在这里创建一个小吃 snack.expo.io 但我没有你的其他文件,你能在这里制作一个小吃并分享你的问题链接,我会修复的。
  • @WaheedAkhtar 感谢您的支持,这将很难重现,但我认为错误在这里: const fetchMoreNotifications = useCallback(async () => { let newPaginationValue = pagination.page + 7; console.log("尝试设置新的分页:" + newPaginationValue); await setPagination({ page: newPaginationValue }); console.log("setted: " + pagination.page); }, [dispatch, setPagination]);当我尝试向下滚动时,第一个控制台日志是:尝试设置新的分页:7,这是正确的,因为我现在想要从 7 得到结果,但是然后“设置:0”

标签: reactjs react-native react-hooks usecallback


【解决方案1】:

问题:

有两个主要问题,一个是page,第二个是notifications,由于useCallbackdependencies,useCallback 函数将始终指向不依赖的旧值,直到其中一个依赖更新。


1)page问题的解决方案:

newPage 作为参数传递给getNotifications,由于setPage 的异步行为,它不会直接更新

第二次,要获取页面的更新值,您可以将page 作为依赖项传递。

2)notification问题的解决方案:

使用setState(prevState =&gt; newState)直接从其上一个状态值更新通知。


解决方案:

  const fetchMoreNotifications = useCallback(async () => {
    const newPage = page + 7;
    setPage(newPage);
    console.log(
      "FETCH MORE from page " + newPage + " on array of " + notifications.length
    );
    getNotifications(newPage).then(() => { // <---- Pass as param
      setIsLoading(false);
    });
  }, [page]); // <---- Add page as dependency 

  const getNotifications = useCallback(
    async page => { // <---- Get page as a param
      setError(null);
      setIsLoading(true);
      try {
        console.log("Get from page " + page);
        // let fromRecord = (page - 1) * 7;
      const retrievedNotifications = await getNotificationList(
        page,
        7,
        true,
        false
      );

      setNotifications(prevNotification => prevNotification.concat(
        retrievedNotifications &&
          retrievedNotifications.response &&
          retrievedNotifications.response.notifications
      )); // <---- Setting up state directly from previous value, instead of getting it from clone version of use callback
      } catch (err) {
        console.log(err);
        setError(err.message);
      }
      setIsLoading(false);
    },
    [setIsLoading, setNotifications, setError]
  );

工作演示

检查控制台日志以获取更新的页面值,通知将呈现在 Html 上

注意:删除了一些代码只是为了提高代码的可读性和 调试问题

【讨论】:

  • 非常感谢,不仅现在它起作用了,而且我也理解了这个错误(这就是我真正想要的)。请注意,我必须在 useEffect(()) 的依赖项上将空数组替换为 [dispatch] 以避免此问题:打开页面 --> useEffect --> getNotifications --> fetchMoreNotifications --> getNotifications。因此,使用 [dispatch] 依赖项,当我打开页面时,“fetchMoreNotifications”不会突然触发
  • 很高兴知道,祝您有美好的一天。 :)
【解决方案2】:

问题其实很简单。 getNotifications 函数是使用useCallback 创建的,并且没有使用notifications 作为依赖项。现在当通知更新时,由于关闭,getNotications 函数仍然引用旧的通知值。

另请注意,您在设置页面状态后立即在 fetchMoreNotifications 上调用 getNotifications,但页面状态也受到关闭的约束,并且不会在相同的重新渲染中更新

这里的解决方案是使用函数的方式setNotifications,使用useEffect在页面变化时触发4r getNotification

const fetchMoreNotifications = useCallback(async () => {
    const newPage = page + 7;
    setPage(newPage);
  }, [dispatch, getNotifications]);

  useEffect(() => {
    setIsLoading(true);
    getNotifications(page).then(() => {
      setIsLoading(false);
    });
  }, [dispatch, page, getNotifications]);

const getNotifications = useCallback(async () => {
    setError(null);
    setIsLoading(true);
    try {
      console.log("Get from page " + page);
      // let fromRecord = (page - 1) * 7;
      const retrievedNotifications = await getNotificationList(
        page,
        7,
        true,
        false
      );

      setNotifications(prevNotification => prevNotification.concat(
        retrievedNotifications &&
          retrievedNotifications.response &&
          retrievedNotifications.response.notifications
      ));
    } catch (err) {
      setError(err.message);
    }
    setIsLoading(false);
  }, [dispatch, setIsLoading, setNotifications, setError]);

【讨论】:

  • 嗨,Shubham,谢谢你的建议,我试试看!你读过上面的cmets吗?我以为问题出在设置新页面,因为它似乎没有更新新分页的新值
  • 哦,是的,很抱歉没有仔细阅读。您如何在fetchMoreNotifications 上调用 getNotifications 函数还有另一个问题我正在更新我的答案
  • 请检查更新,如果它不起作用,请告诉我
  • 很遗憾,它不起作用,但还是谢谢
猜你喜欢
  • 2021-12-25
  • 2021-08-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2020-09-25
相关资源
最近更新 更多