状态已更新,但组件未重新渲染。

huangapple go评论76阅读模式
英文:

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&#243;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(() =&gt; {
let parr = [];
const unsub = database.posts
.orderBy(&quot;createdAt&quot;, &quot;desc&quot;)
.onSnapshot((querySnapshot) =&gt; {
parr = [];
querySnapshot.forEach((doc) =&gt; {
let data = { ...doc.data(), postId: doc.id, expanded: &quot;&quot; };
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) =&gt; {
for (let i = 0; i &lt; posts.length; i++) {
if (posts[i].pId === id) {
if (posts[i].expanded === &quot;&quot;) posts[i].expanded = id;
else posts[i].expanded = &quot;&quot;;
}
}
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(() =&gt; {
console.log(&quot;re render&quot;);
}, [posts]);

Complete code:

export default function Posts({ userData }) {
const [posts, setPosts] = useState([]);
const getDate = (date) =&gt; {
let seconds = date.seconds + date.nanoseconds / 1000000000;
let newDate = new Date(seconds * 1000);
return (
newDate.getDate() +
&quot; &quot; +
newDate.toLocaleString(&quot;default&quot;, { month: &quot;long&quot; })
);
};
const handleComment = (id) =&gt; {
for (let i = 0; i &lt; posts.length; i++) {
if (posts[i].pId === id) {
if (posts[i].expanded === &quot;&quot;) posts[i].expanded = id;
else posts[i].expanded = &quot;&quot;;
}
}
setPosts(posts);
console.log(posts);
};
useEffect(() =&gt; {
console.log(&quot;re render&quot;);
}, [posts]);
useEffect(() =&gt; {
console.log(&quot;here&quot;);
let parr = [];
const unsub = database.posts
.orderBy(&quot;createdAt&quot;, &quot;desc&quot;)
.onSnapshot((querySnapshot) =&gt; {
parr = [];
querySnapshot.forEach((doc) =&gt; {
let data = { ...doc.data(), postId: doc.id, expanded: &quot;&quot; };
parr.push(data);
});
setPosts(parr);
});
return unsub;
}, []);
return (
&lt;div style={{ width: &quot;55%&quot; }}&gt;
{!posts || userData == null ? (
&lt;CircularProgress /&gt;
) : (
&lt;div className=&quot;video-container&quot;&gt;
{posts &amp;&amp; posts.map((post, index) =&gt; {
console.log(post);
return (
&lt;React.Fragment key={index}&gt;
&lt;Card sx={{ maxWidth: 345 }} className=&quot;card-class&quot;&gt;
&lt;CardHeader
avatar={
&lt;Avatar alt=&quot;Remy Sharp&quot; src={userData.profileUrl} /&gt;
}
action={
&lt;IconButton aria-label=&quot;settings&quot;&gt;
&lt;MoreVertIcon /&gt;
&lt;/IconButton&gt;
}
title={userData.fullname}
subheader={getDate(post.createdAt)}
/&gt;
&lt;Video src={post.pURL} /&gt;
&lt;CardActions disableSpacing&gt;
&lt;IconButton aria-label=&quot;add to favorites&quot;&gt;
&lt;Like userData={userData} postData={post} /&gt;
&lt;/IconButton&gt;
&lt;IconButton
onClick={() =&gt; handleComment(post.pId)}
aria-label=&quot;comment&quot;
aria-expanded={post.expanded !== &quot;&quot; ? true : false}
&gt;
&lt;ChatBubbleIcon /&gt;
&lt;/IconButton&gt;
&lt;/CardActions&gt;
&lt;Collapse
in={post.expanded !== &quot;&quot; ? true : false}
timeout=&quot;auto&quot;
unmountOnExit
&gt;
&lt;CardContent&gt;
&lt;Typography paragraph&gt;Method:&lt;/Typography&gt;
&lt;Typography paragraph&gt;
Heat 1/2 cup of the broth in a pot until simmering, add
saffron and set aside for 10 minutes.
&lt;/Typography&gt;
&lt;Typography paragraph&gt;
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&#243;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.
&lt;/Typography&gt;
&lt;Typography paragraph&gt;
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&amp;apos;t open.)
&lt;/Typography&gt;
&lt;Typography&gt;
Set aside off of the heat to let rest for 10 minutes,
and then serve.
&lt;/Typography&gt;
&lt;/CardContent&gt;
&lt;/Collapse&gt;
&lt;/Card&gt;
&lt;/React.Fragment&gt;
);
})}
&lt;/div&gt;
)}
&lt;/div&gt;
);
}

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) =&gt; {
let newPosts = [...posts]
newPosts = posts.forEach((post) =&gt; {
if(post.pId===id)
{
if(post.expanded===&quot;&quot;) post.expanded=id;
else post.expanded=&quot;&quot;;
}
})
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) =&gt; {
  setPosts(posts.map(post =&gt; {
    if (post.pId === id) {
      return {
        ...post,
        expanded: post.expanded ? &quot;&quot; : id
      }
    } else {
      return post
    }
  }))
};

huangapple
  • 本文由 发表于 2023年2月8日 13:50:33
  • 转载请务必保留本文链接:https://go.coder-hub.com/75381806.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定