在通过 websocket 接收的数据的数组数据更改后,reactjs useState 和 useEffect 挂钩不会重新呈现

reactjs useState and useEffect hooks not re-rendering after array data change for data received via websocket

我正在用 reactjs 构建一个需要实时的应用程序,我正在使用 Rails Actioncable 作为 websocket 的包装器。

我可以在创建或更新记录后通过 websocket 接收数据,当我执行控制台日志以查看使用创建的 posts 数组中包含的内容时useHook 但通过 webhook 更新。 post 数组似乎已使用下面显示的代码正确更新。但是 React 不会重新呈现网页,因此用户看不到更新的记录。

import React, {useState, useEffect} from "react"
import consumer from "../../channels/consumer"

const PostList = (props) => {

  const [posts, setPosts] = useState(props.posts || []);

  useEffect(() => {
     consumer.subscriptions.create({ channel: "PostChannel"}, {
       received(data){
         handleRecordUpdate(data);
       }
     })
  }, [posts])
  
  const handleRecordUpdate = (post) => {
    //copying old array
    let getPosts = posts;
    let postIndex = getPosts.findIndex((obj => obj.id == post.id));
    getPosts[postIndex] = post;

   //ensure the new array does not reference the original array using spread operator ...
    setPosts(posts => [...getPosts])
  }

  return (
    <React.Fragment>
      <h3>All Posts</h3>
      <table>
        <thead>
          <tr>
            <th>ID</th>
            <th>Title</th>
            <th>Description</th>
            <th>Is Published</th>
            <th> Actions </th>
          </tr>
        </thead>
        <tbody>
        {
          posts && posts.map((post) => {
            return (
              <tr key={post.id} id={post.id}>
              </tr>
            )
          })
        }
        </tbody>
      </table>
    </React.Fragment>
  );
  
}

这是数组的结构

{
    "posts": [
        {
            "id": 2,
            "title": "second",
            "description": "push",
            "is_published": true,
            "created_at": "2021-04-03T13:59:03.708Z",
            "updated_at": "2021-06-10T10:07:07.783Z"
        },
        {
            "id": 3,
            "title": "third",
            "description": "testing 1",
            "is_published": false,
            "created_at": "2021-04-25T20:02:18.204Z",
            "updated_at": "2021-06-10T10:15:18.724Z"
        },
        {
            "id": 1,
            "title": "good",
            "description": "checkout",
            "is_published": true,
            "created_at": "2021-04-02T16:39:28.568Z",
            "updated_at": "2021-06-10T15:25:42.186Z"
        }
    ]
}

我修复了通过 webhook 更新状态时反应不重新渲染的问题。主要问题是这一行:

const [posts, setPosts] = useState(props.posts || []);

我将该行更改为:

const [posts, setPosts] = useState([]);

通过该更改,这两个方法通过重新呈现状态更新状态并通过 websockets 将状态更改广播到其他打开的选项卡。

方法一:

const handleRecordUpdate = (post) => {
  let updatedPosts = [...posts]
  let old_record = posts.find((item) => item.id === post.id)
  let indexi = posts.indexOf(old_record)
  updatedPosts[indexi] = post;
  setPosts(posts => updatedPosts)
}

您也可以使用深拷贝,它将按照@Mangesh 的建议工作。

const handleRecordUpdate = (post) => {
  let updatedPosts = JSON.parse(JSON.stringify(posts))
  let old_record = posts.find((item) => item.id === post.id)
  let indexi = posts.indexOf(old_record)
  updatedPosts[indexi] = post;
  setPosts(posts => updatedPosts)
}

最后这也行得通

const handleRecordUpdate = (post) => {
  setPosts(posts.map(item => (item.id === post.id ? post : item) ))
}