【问题标题】:Spread operator throwing error in a ReactNative app with redux扩展运算符在使用 redux 的 React Native 应用程序中引发错误
【发布时间】:2021-04-08 07:38:35
【问题描述】:

我正在使用 redux 和 redux thunk 构建一个带有 expo 的 Instagram 克隆,我想实现跟随系统,但是当我切换到个人资料屏幕时,我收到了指向我的减速器的错误。

users.ts(减速器):

import {
  USER_STATE_CHANGE,
  USER_POSTS_STATE_CHANGE,
  USER_FOLLOWING_STATE_CHANGE,
  CLEAR_DATA,
} from "../constants";

const initialState = {
  currentUser: null,
  posts: [],
  following: [],
};

export const user = (state = initialState, action: any) => {
  switch (action.type) {
    case USER_STATE_CHANGE:
      return {
        ...state,
        currentUser: action.currentUser,
      };
    case USER_POSTS_STATE_CHANGE:
      return {
        ...state,
        posts: action.posts,
      };
    case USER_FOLLOWING_STATE_CHANGE:
      return {
        ...state,
        following: action.following,
      };
    case CLEAR_DATA:
      return {
        initialState,
      };

    default:
      return state;
  }
};

我收到以下错误:

TypeError:传播不可迭代实例的无效尝试。 为了可迭代,非数组对象必须有一个 Symbol.iterator 方法。

user.ts(控制用户):

import {
  USER_STATE_CHANGE,
  USER_POSTS_STATE_CHANGE,
  USER_FOLLOWING_STATE_CHANGE,
  CLEAR_DATA,
} from "../constants";

const initialState = {
  currentUser: null,
  posts: [],
  following: [],
};

export const user = (state = initialState, action: any) => {
  switch (action.type) {
    case USER_STATE_CHANGE:
      return {
        ...state,
        currentUser: action.currentUser,
      };
    case USER_POSTS_STATE_CHANGE:
      return {
        ...state,
        posts: action.posts,
      };
    case USER_FOLLOWING_STATE_CHANGE:
      return {
        ...state,
        following: action.following,
      };
    case CLEAR_DATA:
      return {
        initialState,
      };

    default:
      return state;
  }
};

Profile.tsx(个人资料屏幕的代码):

import React, { useState, useEffect } from "react";
import { View, Text, StyleSheet, Image, FlatList, Alert } from "react-native";
import db, { auth } from "../../firebase";
import { Avatar, Button, ActivityIndicator, Colors } from "react-native-paper";
import Navbar from "../shared/Navbar";
import { connect } from "react-redux";

const Profile = ({
  navigation,
  posts,
  route,
  reduxFollowing,
  currentUser,
}: any) => {
  const [user, setUser] = useState<any>(null);
  const [userPosts, setUserPosts] = useState<any>([]);
  const [loading, setLoading] = useState<boolean>(true);
  const [following, setFollowing] = useState<boolean>(false);

  useEffect(() => {
    if (route.params.uid === auth.currentUser?.uid) {
      setUser(auth.currentUser);
      setUserPosts(posts);
      setLoading(false);
    } else {
      db.collection("users")
        .doc(route.params?.uid)
        .get()
        .then((snapshot) => {
          setLoading(false);
          if (snapshot.exists) {
            setUser(snapshot.data());
          } else {
            console.log("does not exist");
          }
        });

      db.collection("posts")
        .doc(route.params.uid)
        .collection("userPosts")
        .orderBy("creation", "desc")
        .onSnapshot((snapshot) => {
          let posts = snapshot.docs.map((doc) => {
            const data = doc.data();
            const id = doc.id;
            //Array.from(data)
            console.log(data);
            //return { ...data, id };
          });
          //setUserPosts(posts);
        });

      if (reduxFollowing.indexOf(route.params.uid) > -1) {
        setFollowing(true);
      } else {
        setFollowing(false);
      }
    }
  }, [route.params.uid, reduxFollowing]);

  const onFollow = () => {
    db.collection("following")
      .doc(auth.currentUser?.uid)
      .collection("userFollowing")
      .doc(route.params.uid)
      .set({})
      .then(() => setFollowing(true))
      .catch((err) =>
        Alert.alert("Opps!, could not Login", err.message, [{ text: "Ok" }])
      );
  };

  const onUnfollow = () => {
    db.collection("following")
      .doc(auth.currentUser?.uid)
      .collection("userFollowing")
      .doc(route.params.uid)
      .delete()
      .then(() => setFollowing(false))
      .catch((err) =>
        Alert.alert("Opps!, could not Login", err.message, [{ text: "Ok" }])
      );
  };

  if (loading) {
    return (
      <View style={styles.loading}>
        <ActivityIndicator size={60} color={Colors.blue500} />
      </View>
    );
  }

  const TwoBtn = () => (
    <>
      {following ? (
        <Button
          style={styles.btn}
          uppercase={false}
          mode="outlined"
          color={Colors.green500}
          onPress={onUnfollow}
        >
          Following
        </Button>
      ) : (
        <Button
          style={styles.btn}
          uppercase={false}
          mode="outlined"
          color="black"
          onPress={onFollow}
        >
          Follow
        </Button>
      )}
      <Button
        style={styles.btn}
        uppercase={false}
        mode="outlined"
        color="black"
        onPress={() => navigation.navigate("Chat")}
      >
        Chat
      </Button>
    </>
  );

  const OneBtn = () => (
    <Button
      style={styles.btn2}
      uppercase={false}
      mode="outlined"
      color="black"
      onPress={() => console.log("")}
    >
      Edit Profile
    </Button>
  );

  return (
    <View style={styles.container}>
      <Navbar
        navigation={navigation}
        title={auth.currentUser?.displayName || currentUser.name}
      />

      <View style={styles.topContainer}>
        <Avatar.Image
          source={user?.photoURL || require("../../assets/Avatar.png")}
        />
        <View style={styles.topRightCont}>
          <Text style={styles.label}>
            Name :{" "}
            <Text style={styles.text}>{user?.displayName || user?.name}</Text>
          </Text>
          <Text style={styles.label}>
            Email : <Text style={styles.text}>{user?.email}</Text>
          </Text>
        </View>
      </View>

      {route.params.uid === auth.currentUser?.uid ? (
        <View style={styles.btnCont}>
          <OneBtn />
        </View>
      ) : (
        <View style={styles.btnCont}>
          <TwoBtn />
        </View>
      )}

      <View style={styles.galleryCont}>
        <FlatList
          keyExtractor={({ item }) => item?.id}
          numColumns={3}
          horizontal={false}
          data={userPosts}
          renderItem={({ item }) => (
            <Image style={styles.image} source={{ uri: item?.downloadURL }} />
          )}
        />
      </View>
    </View>
  );
};

const styles = StyleSheet.create({
//styles
});

const mapStateToProps = (store: any) => ({
  currentUser: store.userState.currentUser,
  posts: store.userState.posts,
  reduxFollowing: store.userState.following,
});

export default connect(mapStateToProps, null)(Profile);

我不知道这是否重要,我正在关注 youtube 教程 (Link to the tutorial)

编辑:

这是发送USERS_DATA_STATE_CHANGE的action

export function getUsersData(uid: any) {
  return (dispatch: any, getState: any) => {
    const found = getState().usersState.users?.some(
      (el: any) => el.uid === uid
    );
    if (!found) {
      db.collection("users")
        .doc(uid)
        .onSnapshot((snapshot) => {
          if (snapshot.exists) {
            let user = snapshot.data();
            if (user) {
              user.uid = snapshot.id;
              dispatch({
                type: USERS_DATA_STATE_CHANGE,
                user,
              });
              dispatch(fetchUsersFollowingPosts(uid));
            }
          } else {
            console.log("does not exist");
          }
        });
    }
  };
}

这是我在那里使用的功能(在操作文件中):

export function fetchUsersFollowingPosts(uid: any) {
  return (dispatch: any, getState: any) => {
    db.collection("posts")
      .doc(uid)
      .collection("userPosts")
      .orderBy("creation", "desc")
      .onSnapshot((snapshot) => {
        const uid = snapshot.query.EP.path.segments[1];
        const user = getState().usersState.users.find(
          (el: any) => el.uid === uid
        );
        let posts = snapshot.docs.map((doc) => {
          const id = doc.id;
          const data = doc.data();
          return { id, ...data, user };
        });
        dispatch({
          type: USERS_POSTS_STATE_CHANGE,
          posts,
          uid,
        });
      });
  };
}

【问题讨论】:

  • users: [...state.users, action.user] --> 通过payload 访问user prop ... action.payload ... users: [...state.users, action.payload] ... 如果你可以包含调度@的动作创建者987654332@那会很好
  • 我已经包含了您询问的代码,以及感谢您的建议,它确实阻止了应用程序在一段时间内抛出错误,但它仍然出现
  • 更新:同样的错误也出现在主屏幕上,但指向同一行
  • 我觉得这里还缺少更多相关代码。必须在某处更改state.users 才能导致此问题。发布其他减速器
  • 也不要这样做 - dispatch(fetchUsersFollowingPosts(uid)); fetchUsers... 函数没有返回一个动作对象,它正在返回另一个函数。不知道这会如何导致您的问题只是要排除另一件事

标签: arrays reactjs react-native redux redux-thunk


【解决方案1】:

首先,您的 initialState 似乎缺少之前的 users: [] prop/val 对 - 需要重新添加。

然后,在您的 reducer 中,您将在此处返回一个新对象:

case CLEAR_DATA:
  return {
    initialState,
  };

这需要传播:

case CLEAR_DATA:
  return {
    ...initialState
  };

否则它就变成{ initialState: { users: [], ...etc } } 而不是{ users: [], ...etc }

【讨论】:

  • 也可能只是case CLEAR_DATA: return initialState
  • 技术上是的,但通常您希望每次都提供一个新状态,以防止出现意外的突变。如果你只是返回initialState,那么你在某个地方直接用getState().usersState.new_value = true 改变它,现在new_value: true 与每个后续的CLEAR_DATA 动作一起使用。更多详细信息的文档 - redux.js.org/tutorials/fundamentals/…
  • 感谢您帮助我,但这又造成了一个错误:TypeError: Cannot read property 'uid' of undefined 及其指向USERS_POSTS_STATE_CHANGE
  • 在该行上方放置一个断点并观察值是什么,找出调用该函数的函数,并追踪它丢失的位置
  • @Deryck 谢谢你,但是通过像getState().usersState.new_value = true 这样的方式直接mututing状态是非常罕见的......通常我们会拿一个全新的副本并使用它......@ 987654336@ 有点像return state; 我们调用default switch case
猜你喜欢
  • 2020-04-06
  • 1970-01-01
  • 2020-01-26
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2017-01-12
  • 2019-03-13
相关资源
最近更新 更多