【问题标题】:Component not re-rendering on useState array change组件不会在 useState 数组更改时重新渲染
【发布时间】:2020-08-22 21:03:02
【问题描述】:

我在FeedScreen.js 上显示的“推特”卡片上有一个收藏按钮。

~~~~~~~~~ IMPORTS SNIP ~~~~~~~~~

function FeedScreen(props) {
  const [feed, setFeed] = useState([]);
  const [favorites, setFavorite] = useState([]);
  const [refreshing, setRefreshing] = useState(false);
  useEffect(() => {
    loadFeed(0, 4);
  }, []);

  const loadFeed = async (last_id = 0, limit = 1) => {
    setRefreshing(true);
    const response = await tweetsApi.getTweets(last_id, limit);
    if (response.ok) {
      setFeed(response.data["data"].concat(feed));
    } else {
      console.log(response.problem);
    }
    setRefreshing(false);
  };

  const handleBookmark = async (item_id) => {
    const response = await tweetsApi.toggleBookmark(item_id);
    if (response.ok) {
      console.log("ok response");
      setFavorite(favorites.concat(item_id));
// I've tried this as well
// setFavorite([...favorites].concat(item_id));
// but in vain
      console.log(favorites);
    }
  };

  return (
    <Screen style={styles.screen}>
      <FlatList
        data={feed}
        keyExtractor={(tweet) => {
          return tweet.id.toString();
        }}
        renderItem={({ item }) => (
~~~~~~~~~ SNIP ~~~~~~~~~
            <CardFooter
              style={{ marginLeft: 20 }}
              item={item}
              onPress={handleBookmark}
            />
          </View>
        )}
        ItemSeparatorComponent={ListItemSeparator}
        refreshing={refreshing}
        onRefresh={() => {
          loadFeed(feed[0]["id"], 2);
        }}
      />
    </Screen>
  );
}

~~~~~~~~~ SNIP ~~~~~~~~~

这是CardFooter.js

~~~~~~~~~ SNIP ~~~~~~~~~

function CardFooter({ item, onPress }) {
  return (
      <View style={styles.bookmark}>
        <TouchableOpacity
          onPress={() => {
            return onPress(item.id);
          }}
        >
          {item.bookmarked && (
            <FontAwesome name="bookmark" size={24} color="red" />
          )}
          {!item.bookmarked && (
            <FontAwesome5 name="bookmark" size={24} color="black" />
          )}
        </TouchableOpacity>
      </View>
    </View>
  );
}
export default CardFooter;
~~~~~~~~~ SNIP ~~~~~~~~~

但是组件似乎没有重新渲染。 我看过这些:

  1. react-component-not-re-rendering-after-using-usestate-hook
  2. 类似here
  3. Another one 17 days back - why-usestate-is-not-re-rendering
  4. usestate-not-re-rendering-when-updating-nested-object

所有这些和其他类似的,它们中的每一个都指向一个事实,即应该创建一个新数组,以便 react 重新渲染它。

更新

console.log 输出

是的,console.log 正在打印数组,尽管之前有一个值。那是因为useState 是异步的,所以它不会打印实时数组。所以,当第二次调用它时,它会显示一个item_id(前一个)添加到favorites

【问题讨论】:

  • console.log()你的回复是不是真的返回了数据?
  • console.log(favorites) 显示什么?
  • @Ross 更新了问题,console.log 输出

标签: react-native react-hooks use-state


【解决方案1】:

我终于通过管理组件本身的状态解决了这个问题。

不确定这是否是执行此操作的“正确方法”,但请阅读here (how-to-add-a-simple-toggle-function-in-react-native) ,您可以这样做。

所以,现在书签组件从*组件 (FeedScreen.js) 获得响应:

const handleBookmark = async (item_id) => {
    const response = await tweetsApi.toggleBookmark(item_id);
    if (response.ok) {
      return true;
    } else {
      return false;
    }
  };

并更改CardFooter.js,即书签组件所在的位置。

function CardFooter({ item, onPress }) {
  const [favorite, setFavorite] = useState(item.bookmarked);
  return (
      <View style={styles.bookmark}>
        <TouchableOpacity
          onPress={async () => {
            let response = await onPress(item.id);
            if (response) {
              setFavorite(!favorite);
            } else {
              alert("Some error occurred");
            }
          }}
        >
          {favorite && <FontAwesome name="bookmark" size={24} color="red" />}
          {!favorite && (
            <FontAwesome5 name="bookmark" size={24} color="black" />
          )}
        </TouchableOpacity>
      </View>
    </View>
  );
}

担忧

我有点担心在这个组件中处理响应。

我应该在底部组件中处理async 操作吗?

【讨论】: