#arrays #reactjs #react-native #redux #redux-thunk
#массивы #reactjs #react-native #уменьшение #redux-thunk
Вопрос:
Я создаю клон Instagram с expo, используя redux и redux thunk, я хотел внедрить систему подписки, но при переключении на экран профиля я получаю сообщение об ошибке, указывающее на мои редукторы.
users.ts (reducer) :
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;
}
};
Я получаю следующую ошибку :
Ошибка типа: недопустимая попытка распространения не итерируемого экземпляра. Чтобы быть итеративными, объекты, не являющиеся массивами, должны иметь метод 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 (ссылка на руководство)
Редактировать :
Это действие, которое отправляет USERS_DATA_STATE_CHANGE
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,
});
});
};
}
Комментарии:
1.
users: [...state.users, action.user]
—> доступuser
черезpayload
prop … Экшен. полезная нагрузка …users: [...state.users, action.payload]
… и если бы вы могли включить создателя действия, который отправляетUSERS_DATA_STATE_CHANGE
, это было бы неплохо2. Я включил код, который вы просили, плюс спасибо за ваше предложение, это предотвратило выдачу ошибки приложением еще на некоторое время, но она пока не появляется
3. ОБНОВЛЕНИЕ: та же ошибка появляется и на главном экране, но указывает на ту же строку
4. я чувствую, что здесь отсутствует еще более релевантный код.
state.users
что-то должно где-то измениться, чтобы вызвать это. опубликуйте другие редукторы5. также не делайте этого —
dispatch(fetchUsersFollowingPosts(uid));
функция fetchUsers … возвращает не объект действия, а другую функцию. не понимаю, как это может вызвать вашу проблему, просто нужно исключить еще одну вещь
Ответ №1:
Во-первых, вам initialState
, похоже, не хватает users: []
пары prop / val, которая была у него раньше — нужно добавить ее обратно.
Затем в вашем редукторе вы возвращаете здесь новый объект:
case CLEAR_DATA:
return {
initialState,
};
Это необходимо распространить:
case CLEAR_DATA:
return {
...initialState
};
в противном случае это становится просто { initialState: { users: [], ...etc } }
вместо { users: [], ...etc }
.
Комментарии:
1. Или это может быть просто
case CLEAR_DATA: return initialState
2. технически да, но обычно вы хотите каждый раз предоставлять новое состояние, чтобы предотвратить неожиданную мутацию в будущем. если вы просто вернетесь
initialState
, то где-то вы мутируете его напрямую сgetState().usersState.new_value = true
помощью nownew_value: true
, при каждом последующемCLEAR_DATA
действии. документы для получения более подробной информации — redux.js.org/tutorials/fundamentals /…3. Спасибо, что помогли мне, но это создает еще одну ошибку:
TypeError: Cannot read property 'uid' of undefined
и она указывает наUSERS_POSTS_STATE_CHANGE
4. поместите точку останова над этой строкой и посмотрите, каковы значения, узнайте, что вызвало функцию, чтобы доставить вас туда, и отследите, где она отсутствует
5. @Deryck Спасибо, но прямое изменение состояния, выполняя что-то подобное
getState().usersState.new_value = true
, очень необычно… обычно мы берем свежую новую копию и играем с ней…return initialState
похоже на тоreturn state;
, что мы вызываемdefault switch case