英文:
State is updated but component is not getting Re-rendered
问题
I am using MUI card to display all the posts of logged in user. The MUI card has property of expand or collapse depending upon true
or false
. On each card I am giving key expanded
which is set to empty string initially. On click of icon I am setting expanded
of that particular post to non-empty string. If expanded field is non-empty string then I am setting true in collapse else false.
The state is getting updated as expected but component is not getting re-render and as a result expand or collapse is not working.
On initial render I am setting the post state and adding expanded field to it.
useEffect(() => {
let parr = [];
const unsub = database.posts
.orderBy("createdAt", "desc")
.onSnapshot((querySnapshot) => {
parr = [];
querySnapshot.forEach((doc) => {
let data = { ...doc.data(), postId: doc.id, expanded: "" };
parr.push(data);
});
setPosts(parr);
});
return unsub;
}, []);
Function called handleComment
is responsible for setting expanded
to empty string or non-empty string.
const handleComment = (id) => {
for (let i = 0; i < posts.length; i++) {
if (posts[i].pId === id) {
if (posts[i].expanded === "") posts[i].expanded = id;
else posts[i].expanded = "";
}
}
setPosts(posts);
console.log(posts);
};
A useEffect
is set on posts which should be called whenever there is change posts state. But it is not getting called.
useEffect(() => {
console.log("re render");
}, [posts]);
Complete code:
export default function Posts({ userData }) {
const [posts, setPosts] = useState([]);
const getDate = (date) => {
let seconds = date.seconds + date.nanoseconds / 1000000000;
let newDate = new Date(seconds * 1000);
return (
newDate.getDate() +
" " +
newDate.toLocaleString("default", { month: "long" })
);
};
const handleComment = (id) => {
for (let i = 0; i < posts.length; i++) {
if (posts[i].pId === id) {
if (posts[i].expanded === "") posts[i].expanded = id;
else posts[i].expanded = "";
}
}
setPosts(posts);
console.log(posts);
};
useEffect(() => {
console.log("re render");
}, [posts]);
useEffect(() => {
console.log("here");
let parr = [];
const unsub = database.posts
.orderBy("createdAt", "desc")
.onSnapshot((querySnapshot) => {
parr = [];
querySnapshot.forEach((doc) => {
let data = { ...doc.data(), postId: doc.id, expanded: "" };
parr.push(data);
});
setPosts(parr);
});
return unsub;
}, []);
return (
<div style={{ width: "55%" }}>
{!posts || userData == null ? (
<CircularProgress />
) : (
<div className="video-container">
{posts && posts.map((post, index) => {
console.log(post);
return (
<React.Fragment key={index}>
<Card sx={{ maxWidth: 345 }} className="card-class">
<CardHeader
avatar={
<Avatar alt="Remy Sharp" src={userData.profileUrl} />
}
action={
<IconButton aria-label="settings">
<MoreVertIcon />
</IconButton>
}
title={userData.fullname}
subheader={getDate(post.createdAt)}
/>
<Video src={post.pURL} />
<CardActions disableSpacing>
<IconButton aria-label="add to favorites">
<Like userData={userData} postData={post} />
</IconButton>
<IconButton
onClick={() => handleComment(post.pId)}
aria-label="comment"
aria-expanded={post.expanded !== "" ? true : false}
>
<ChatBubbleIcon />
</IconButton>
</CardActions>
<Collapse
in={post.expanded !== "" ? true : false}
timeout="auto"
unmountOnExit
>
<CardContent>
<Typography paragraph>Method:</Typography>
<Typography paragraph>
Heat 1/2 cup of the broth in a pot until simmering, add
saffron and set aside for 10 minutes.
</Typography>
<Typography paragraph>
Heat oil in a (14- to 16-inch) paella pan or a large,
deep skillet over medium-high heat. Add chicken, shrimp
and chorizo, and cook, stirring occasionally until
lightly browned, 6 to 8 minutes. Transfer shrimp to a
large plate and set aside, leaving chicken and chorizo
in the pan. Add pimentón, bay leaves, garlic, tomatoes,
onion, salt and pepper, and cook, stirring often until
thickened and fragrant, about 10 minutes. Add saffron
broth and remaining 4 1/2 cups chicken broth; bring to a
boil.
</Typography>
<Typography paragraph>
Add rice and stir very gently to distribute. Top with
artichokes and peppers, and cook without stirring, until
most of the liquid is absorbed, 15 to 18 minutes. Reduce
heat to medium-low, add reserved shrimp and mussels,
tucking them down into the rice, and cook again without
stirring, until mussels have opened and rice is just
tender, 5 to 7 minutes more. (Discard any mussels that
don't open.)
</Typography>
<Typography>
Set aside off of the heat to let rest for 10 minutes,
and then serve.
</Typography>
</CardContent>
</Collapse>
</Card>
</React.Fragment>
);
})}
</div>
)}
</div>
);
}
I have also tried using forEach
and here posts is getting updated and component is getting re-rendered but posts are not getting displayed. Only CircularProgress
is moving.
const handleComment = (id) => {
let newPosts = [...posts]
newPosts = posts.forEach((post) => {
if(post.pId===id)
{
if(post.expanded==="") post.expanded=id;
else post.expanded="";
}
})
setPosts(newPosts);
console.log(posts);
};
I tried setting expanded to non-empty string if it is empty string using handleComment
function. It is working fine but the component is not getting re-render as a result expanded
remains empty string.
And in the second case of forEach
posts state is getting updated and component is also getting re-rendered but only CircularProgress is moving and posts are not getting displayed.
英文:
I am using MUI card to display all the posts of logged in user. The MUI card has property of expand or collapse depending upon true
or false
. On each card I am giving key expanded
which is set to empty string initially. On click of icon I am setting expanded
of that particular post to non-empty string. If expanded field is non-empty string then I am setting true in collapse else false.
The state is getting updated as expected but component is not getting re-render and as a result expand or collapse is not working.
On initial render I am setting the post state and adding expanded field to it.
useEffect(() => {
let parr = [];
const unsub = database.posts
.orderBy("createdAt", "desc")
.onSnapshot((querySnapshot) => {
parr = [];
querySnapshot.forEach((doc) => {
let data = { ...doc.data(), postId: doc.id, expanded: "" };
parr.push(data);
});
setPosts(parr);
});
return unsub;
}, []);
Function called handleComment
is responsible for setting expanded
to empty string or non-empty string.
const handleComment = (id) => {
for (let i = 0; i < posts.length; i++) {
if (posts[i].pId === id) {
if (posts[i].expanded === "") posts[i].expanded = id;
else posts[i].expanded = "";
}
}
setPosts(posts);
console.log(posts);
};
A useEffect
is set on posts which should be called whenever there is change posts state. But it is not getting called.
useEffect(() => {
console.log("re render");
}, [posts]);
Complete code:
export default function Posts({ userData }) {
const [posts, setPosts] = useState([]);
const getDate = (date) => {
let seconds = date.seconds + date.nanoseconds / 1000000000;
let newDate = new Date(seconds * 1000);
return (
newDate.getDate() +
" " +
newDate.toLocaleString("default", { month: "long" })
);
};
const handleComment = (id) => {
for (let i = 0; i < posts.length; i++) {
if (posts[i].pId === id) {
if (posts[i].expanded === "") posts[i].expanded = id;
else posts[i].expanded = "";
}
}
setPosts(posts);
console.log(posts);
};
useEffect(() => {
console.log("re render");
}, [posts]);
useEffect(() => {
console.log("here");
let parr = [];
const unsub = database.posts
.orderBy("createdAt", "desc")
.onSnapshot((querySnapshot) => {
parr = [];
querySnapshot.forEach((doc) => {
let data = { ...doc.data(), postId: doc.id, expanded: "" };
parr.push(data);
});
setPosts(parr);
});
return unsub;
}, []);
return (
<div style={{ width: "55%" }}>
{!posts || userData == null ? (
<CircularProgress />
) : (
<div className="video-container">
{posts && posts.map((post, index) => {
console.log(post);
return (
<React.Fragment key={index}>
<Card sx={{ maxWidth: 345 }} className="card-class">
<CardHeader
avatar={
<Avatar alt="Remy Sharp" src={userData.profileUrl} />
}
action={
<IconButton aria-label="settings">
<MoreVertIcon />
</IconButton>
}
title={userData.fullname}
subheader={getDate(post.createdAt)}
/>
<Video src={post.pURL} />
<CardActions disableSpacing>
<IconButton aria-label="add to favorites">
<Like userData={userData} postData={post} />
</IconButton>
<IconButton
onClick={() => handleComment(post.pId)}
aria-label="comment"
aria-expanded={post.expanded !== "" ? true : false}
>
<ChatBubbleIcon />
</IconButton>
</CardActions>
<Collapse
in={post.expanded !== "" ? true : false}
timeout="auto"
unmountOnExit
>
<CardContent>
<Typography paragraph>Method:</Typography>
<Typography paragraph>
Heat 1/2 cup of the broth in a pot until simmering, add
saffron and set aside for 10 minutes.
</Typography>
<Typography paragraph>
Heat oil in a (14- to 16-inch) paella pan or a large,
deep skillet over medium-high heat. Add chicken, shrimp
and chorizo, and cook, stirring occasionally until
lightly browned, 6 to 8 minutes. Transfer shrimp to a
large plate and set aside, leaving chicken and chorizo
in the pan. Add pimentón, bay leaves, garlic, tomatoes,
onion, salt and pepper, and cook, stirring often until
thickened and fragrant, about 10 minutes. Add saffron
broth and remaining 4 1/2 cups chicken broth; bring to a
boil.
</Typography>
<Typography paragraph>
Add rice and stir very gently to distribute. Top with
artichokes and peppers, and cook without stirring, until
most of the liquid is absorbed, 15 to 18 minutes. Reduce
heat to medium-low, add reserved shrimp and mussels,
tucking them down into the rice, and cook again without
stirring, until mussels have opened and rice is just
tender, 5 to 7 minutes more. (Discard any mussels that
don&apos;t open.)
</Typography>
<Typography>
Set aside off of the heat to let rest for 10 minutes,
and then serve.
</Typography>
</CardContent>
</Collapse>
</Card>
</React.Fragment>
);
})}
</div>
)}
</div>
);
}
I have also tried using forEach
and here posts is getting updated and component is getting re-rendered but posts are not getting displayed. Only CircularProgress
is moving.
const handleComment = (id) => {
let newPosts = [...posts]
newPosts = posts.forEach((post) => {
if(post.pId===id)
{
if(post.expanded==="") post.expanded=id;
else post.expanded="";
}
})
setPosts(newPosts);
console.log(posts);
};
I tried setting expanded to non-empty string if it is empty string using handleComment
function. It is working fine but the component is not getting re-render as a result expanded
remains empty string.
And in the second case of forEach
posts state is getting updated and component is also getting re-rendered but only CircularProgress is moving and posts are not getting displayed.
答案1
得分: 0
我认为它没有更新是因为您对posts
数组进行了突变,而不是创建一个新的数组。就setPosts()
而言,您已经传递了相同的数组,所以不需要重新渲染(参见更新状态中的数组)。您可以使用map()
来创建一个新的数组并同时执行逻辑。
const handleComment = (id) => {
setPosts(posts.map(post => {
if (post.pId === id) {
return {
...post,
expanded: post.expanded ? "" : id
}
} else {
return post
}
}))
};
英文:
I believe it's not updating because you're mutating the posts
array instead of creating a new one. As far as setPosts()
is concerned you've passed it the same array so there's no re-render needed (see Updating Arrays in State). You could use map()
to create a new array and perform the logic at the same time.
const handleComment = (id) => {
setPosts(posts.map(post => {
if (post.pId === id) {
return {
...post,
expanded: post.expanded ? "" : id
}
} else {
return post
}
}))
};
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。
评论