I have a collection of profile documents in firebase and I want to render them in the profiles page, however after I have updated the userProfiles state and use useDispatch to store the state in the slice, I get an infinite loop when rendering the profile page.
I have tried putting the dispatch() into a useEffect, not in a useEffect and inside the querySnapshot promise but I'm still getting an infinite loop wherever I put it.
Any feedback is appreciated, thank you.
\\ profiles.js
export const Profiles = () => {
const [userProfiles, setUserProfiles] = useState([]);
const dispatch = useDispatch();
const navigate = useNavigate();
const user = useSelector(selectUser);
db.collection("customers")
.doc(user.info.uid)
.collection("profiles")
.get()
.then((querySnapshot) => {
const documents = querySnapshot.docs.map((doc) => doc.data());
setUserProfiles(documents);
});
useEffect(() => {
dispatch(profiles(userProfiles));
}, []);
console.log({ userProfiles });
return (
<div className="profile_container">
<h1 className="profile_title">Who's Watching?</h1>
<div className="profile_row">
{userProfiles.map((profile) => {
return (
<div className="profile_individualProfile">
<img
src="https://occ-0-300-1167.1.nflxso.net/dnm/api/v6/K6hjPJd6cR6FpVELC5Pd6ovHRSk/AAAABY5cwIbM7shRfcXmfQg98cqMqiZZ8sReZnj4y_keCAHeXmG_SoqLD8SXYistPtesdqIjcsGE-tHO8RR92n7NyxZpqcFS80YfbRFz.png?r=229"
alt="profile"
/>
<p>{profile.name}</p>
</div>
);
})}
<div
onClick={() => navigate("/add-profile")}
className="profile_addProfile_container"
>
<img
src="https://img.icons8.com/ios-glyphs/30/FFFFFF/plus--v1.png"
alt="add profile"
/>
<h2>Add Profile</h2>
</div>
</div>
</div>
);
};
\\ userSlice.js
export const userSlice = createSlice({
name: "user",
initialState: {
user: {
info: null,
profiles: [],
},
},
reducers: {
login: (state, action) => {
state.user.info = action.payload;
},
logout: (state) => {
state.user.info = null;
},
profiles: (state, action) => {
state.user.profiles.push(action.payload);
},
},
});
CodePudding user response:
In the current implementation, when your page is rendered, db.collections
runs and you set state setUserProfiles(documents)
which renders your app and again db.collections
runs. to prevent this you should run db.collections
in useEffect
.
// fetch users only when your app renders
useEffect(() => {
db.collection("customers")
.doc(user.info.uid)
.collection("profiles")
.get()
.then((querySnapshot) => {
const documents = querySnapshot.docs.map((doc) => doc.data());
setUserProfiles(documents);
});
}, []);
have another useEffect
useEffect(() => {
dispatch(profiles(userProfiles));
}, [userProfiles]);
this will NOT work neither. setUserProfiles
will be causing issue. Because when app renders, you fetch data, you set the state, change the userProfiles
, this will rerender app again.
The problem with your code is you do not need setUserProfiles
. instead in db.collections()
when you get the documents, you dispatch the documents and then access the profiles from redux with useSelector
// fetch users only when your app renders
useEffect(() => {
db.collection("customers")
.doc(user.info.uid)
.collection("profiles")
.get()
.then((querySnapshot) => {
const documents = querySnapshot.docs.map((doc) => doc.data());
// setUserProfiles(documents); You do not need this
dispatch(profiles(userProfiles))
});
}, []);
Now use useSelector
to reach the state in redux
// assuming reducers name is "users"
const usersState = useSelector((state) => state.users);
now when you use map guard your app
// make sure you use the correct data
// you migh need to destructure
{usersState && usersState.map((profile) => {
CodePudding user response:
For anyone that runs into this issue you may find this useful. Following from yilmaz's helpful answer, I had to update the Profiles.js and userSlice.js as follows...
// Profiles.js
export const Profiles = () => {
const dispatch = useDispatch();
const navigate = useNavigate();
const usersState = useSelector(profiles);
useEffect(() => {
db.collection("customers")
.doc(usersState.payload.user.user.info.uid)
.collection("profiles")
.get()
.then((querySnapshot) => {
const documents = querySnapshot.docs.map((doc) => doc.data());
!usersState.payload.user.user.profiles.includes((arr) =>
documents.every(arr)
) && dispatch(profiles(documents));
});
}, []);
return (
<div className="profile_container">
<h1 className="profile_title">Who's Watching?</h1>
<div className="profile_row">
{usersState.payload.user.user.profiles.map((profile) => {
console.log(profile);
return (
<div className="profile_individualProfile">
<img
src="https://occ-0-300-1167.1.nflxso.net/dnm/api/v6/K6hjPJd6cR6FpVELC5Pd6ovHRSk/AAAABY5cwIbM7shRfcXmfQg98cqMqiZZ8sReZnj4y_keCAHeXmG_SoqLD8SXYistPtesdqIjcsGE-tHO8RR92n7NyxZpqcFS80YfbRFz.png?r=229"
alt="profile"
/>
<p>{profile.name}</p>
</div>
);
})}
<div
onClick={() => navigate("/add-profile")}
className="profile_addProfile_container"
>
<img
src="https://img.icons8.com/ios-glyphs/30/FFFFFF/plus--v1.png"
alt="add profile"
/>
<h2>Add Profile</h2>
</div>
</div>
</div>
);
};
// userSlice.js
export const userSlice = createSlice({
name: "user",
initialState: {
user: {
info: null,
profiles: [],
},
},
reducers: {
login: (state, action) => {
state.user.info = action.payload;
},
logout: (state) => {
state.user.info = null;
},
profiles: (state, action) => {
state.user.profiles.length = 0;
state.user.profiles.push(...action.payload);
},
},
});