I want to get 3 random (but different from each other) user data using useSelector and display these data on the screen.but it always renders unnecessarily and I can't reach the result I wanted.Unfortunately I couldn't find where I made the mistake.
The final version of my code is as follows;
import * as React from "react";
import Avatar from "@mui/material/Avatar";
import Stack from "@mui/material/Stack";
import Box from "@mui/material/Box";
import Button from "@mui/material/Button";
import Typography from "@mui/material/Typography";
import { useSelector } from "react-redux";
import "./whoToFollow.css";
export default function WoToFollow() {
const { authReducer, userReducer } = useSelector((state) => state);
const authUser = authReducer?.user?.userId;
const userList = userReducer?.data;
var arrayMyUser = [];
console.log("arrayMyUser =>>", arrayMyUser);
var selected = [];
// console.log("selected =>>", selected);
function arrayUserList(callback) {
setTimeout(function () {
var arrayUsers = userList?.filter((user) => user?.userId !== authUser);
arrayUsers?.map((item) => arrayMyUser.push(item));
}, 1000);
callback();
}
function rand() {
setTimeout(function () {
for (var i = 0; i < 3; i ) {
var ran = arrayMyUser[Math.floor(Math.random() * arrayMyUser?.length)];
console.log("ran =>> ", ran);
if (selected.indexOf(ran) === -1) selected.push(ran);
}
}, 1000);
}
arrayUserList(rand);
return (
<Stack direction="row" spacing={2}>
{selected &&
selected?.map((p) => (
<Box className="whotofollowavatar">
<Avatar
className="whotoFollowAvatar"
alt={p?.name}
src={p?.avatar}
/>
<Typography variant="overline" display="block" gutterBottom>
{p?.name p?.surname}
</Typography>
<Button className="whotoFollowButton" variant="contained">
Follow
</Button>
</Box>
))}
</Stack>
);
}
I tried to get three random data, but each time I got a different error. I got "undefined" for the first time, then I couldn't get the data randomly and Sometimes I didn't get any results because the data came late.. Finally, the data sometimes comes as I want, sometimes it doesn't come at all.
CodePudding user response:
you have some problems and you are doing everything really complicated, some key points to have in mind using React:
- every piece of code besides hooks inside a function component will run on every render, that means every variable you define will have a new value on every render (except for hooks)
- if you need to store a value between renders, use a hook (useState or useRef are the most basic)
Here is your component using the useEffect useState to store the suggestions list, (also you had a typo in the component name):
function WhoToFollow() {
const authUser = 3; // this is from your reducer
const userList = useRef([...dummyUserList]); // this is from your reducer, ignore the useRef, is for testing
const [suggestionList, setSuggestionList] = useState<any>([]); // replace any with the correct type
useEffect(() => {
if (authUser && userList) {
setSuggestionList(() => {
const { current: completeUserList } = userList;
const filteredList = completeUserList.filter(
(user) => user?.userId !== authUser
);
const newSuggestionList = [];
while (newSuggestionList.length < 3) {
const ran =
filteredList[Math.floor(Math.random() * filteredList?.length)];
if (newSuggestionList.indexOf(ran) === -1)
newSuggestionList.push(ran);
}
return newSuggestionList;
});
}
}, [userList, authUser]);
return (
<div>
userList:
{suggestionList.map((user: any) => (
<div>{user.name}</div>
))}
</div>
);
}
What I did:
- store the generated list inside an useState
- move all the logic to the useEffect and remove the functions, you don't need those
- refactor the logic to generate 3 suggestions using a while, with the for it will not generate 3 suggestions every time, if you find a duplicated, it will not add a new one
Here is a codesandbox:
https://codesandbox.io/s/kind-silence-3e5s80?file=/src/App.tsx
CodePudding user response:
You can try this, encapsulating the function within a useEffect
hook. Also i feel like you it would be best to have states for arrayMyUser, and selected. That will be the "React" way of handling things.
import * as React from "react";
import Avatar from "@mui/material/Avatar";
import Stack from "@mui/material/Stack";
import Box from "@mui/material/Box";
import Button from "@mui/material/Button";
import Typography from "@mui/material/Typography";
import { useSelector } from "react-redux";
import "./whoToFollow.css";
export default function WoToFollow() {
const { authReducer, userReducer } = useSelector((state) => state);
const authUser = authReducer?.user?.userId;
const userList = userReducer?.data;
var arrayMyUser = [];
console.log("arrayMyUser =>>", arrayMyUser);
var selected = [];
// console.log("selected =>>", selected);
function arrayUserList(callback) {
setTimeout(function () {
var arrayUsers = userList?.filter((user) => user?.userId !== authUser);
arrayUsers?.map((item) => arrayMyUser.push(item));
}, 1000);
callback();
}
function rand() {
setTimeout(function () {
for (var i = 0; i < 3; i ) {
var ran = arrayMyUser[Math.floor(Math.random() * arrayMyUser?.length)];
console.log("ran =>> ", ran);
if (selected.indexOf(ran) === -1) selected.push(ran);
}
}, 1000);
}
useEffect(() => {
if (authUser && userList) {
arrayUserList(rand);
}
}, [authUser, userList]);
return (
<Stack direction="row" spacing={2}>
{selected &&
selected?.map((p) => (
<Box className="whotofollowavatar">
<Avatar
className="whotoFollowAvatar"
alt={p?.name}
src={p?.avatar}
/>
<Typography variant="overline" display="block" gutterBottom>
{p?.name p?.surname}
</Typography>
<Button className="whotoFollowButton" variant="contained">
Follow
</Button>
</Box>
))}
</Stack>
);
}