【问题标题】:Re-render when array of objects property changes using React hook useEffect()当使用 React 钩子 useEffect() 更改对象数组时重新渲染
【发布时间】:2021-06-17 18:52:28
【问题描述】:

我基本上是在制作一个模仿 Craigslist 的 React 应用程序。登录的用户可以查看作为销售或服务项目的组件。

用户可以点击组件来获取详细信息,这也会呈现一个评论表单。提交表单时,Sale 对象属性“cmets”被更新。但由于引用没有更新,useEffect() 不会触发重新渲染。新评论显示时手动刷新。

我知道我可能需要使用 .map() 创建一个新引用并将状态分配给新引用,但未能成功地将其正确化。

钩子返回一个像这样的对象(里面有'cmets'对象数组):

{
  "sale": {
    "itemName": "Legos",
    "itemInfo": "Dont step on them!",
    "price": 33,
    "postedBy": testUser,
    "likedBy": null,
    "createdAt": "2021-06-15T13:20:16.508Z",
    "comments": [
      {
        "commentText": "How many?",
        "postedBy": "davey",
        "createdAt": "2021-06-17T16:52:55.926Z",
        "id": 39
      },
      {
        "commentText": "Could you come down on price?!",
        "postedBy": "testUser2",
        "createdAt": "2021-06-17T17:14:48.155Z",
        "id": 40
      }
    ]
  }
}

这是我调用 useEffect() 的代码:

function SalesDetail() {
    const { id } = useParams();
    const currentUser = useContext(UserContext);
    const route = 'sales'

    const [sale, setSale] = useState()

    useEffect(function getSaleById() {

        async function getSale(){
            let sale = (await BoonelistApi.getSale(id))
            setSale(sale)
        }
        getSale();
    }, [id]);

我尝试让 sale.cmets 成为钩子中的依赖项,但我似乎仍然无法理解它和“不要改变状态”的格言。我也在 Stack 上查看过类似的问题,但无法让它为我工作。

非常感谢任何帮助,如果需要更多详细信息,请告诉我。

【问题讨论】:

  • 当有新评论时,组件中id const 的值是否会改变?
  • the Sale object property 'comments' is updated. But since the reference is not updated, useEffect() does not trigger a re-render. 你能说明你是如何更新 cmets 的吗?
  • id 取自参数,以提供对数据库中销售项目的引用,作为 sales_id。每个 sales_comment 也有自己的唯一 ID。

标签: javascript reactjs react-hooks use-effect use-state


【解决方案1】:

您可以使用提供者模式进行重新渲染。按照下面的代码行我想它会对你有所帮助

// Provider.js

const SaleContext = createContext({
   sale :{},
   getSale :()=>{},
   setSaleId :()=>{}
});

const SaleProvider = ({children})=>{
const [id,setId] = useState('') // string or number
 const [sale,setSale] = useState({})
 
 const getSaleById = async (id) =>{
    let sale = (await BoonelistApi.getSale(id));
    return sale;
}

 const getSale = (id) => setSale(getSaleById(id));
 const setSaleId = id => setId(id);
 
useEffect(()=>{
 getSale(id);
},[id])

return (
 <SaleContext.Provider values = {{
 sale,
getSale,
setSaleId
}}>
{children}
</SaleContext.Provider>

)

}
export default SaleProvider;
function SalesDetail() {

    const [saleList, setSale] = useState();
    const { id } = useParams();

    const {sale,getSale,setSaleId} = useContext(SaleContext);
    const currentUser = useContext(UserContext);
    const route = 'sales'

    
    
    const getSaleById = () => {
     setSaleId(id);
     setSale(sale)
} // every time this function gets called the useEffect will trigger in the provider.js
<SaleProvider>
App.js
</SaleProvider>

更多关于Provider Pattern。我想这对你有点帮助。此外,您可以使用 Redux 或 Saga 来管理您的状态。

【讨论】:

  • 非常感谢。我认为这会起作用,但我只是在表单提交时手动调用 getSale(id) 。我对这个提交有点时间紧迫,并决定继续这样做,但我将你的答案添加到我的笔记中,因为我还需要更多练习 useContext 。再次感谢!
【解决方案2】:

看起来您正在渲染“销售”状态属性。当嵌套状态对象(在本例中为“cmets”属性)发生变化时,React 不会检测到更新的状态并重新渲染。您可以使用许多结构来获得所需的重新渲染,请记住,组件道具的更改将始终触发重新渲染,并且状态最好用作平面非嵌套键:值对。我将为每个销售项目创建一个“cmets”组件,并通过来自父销售项目 &lt;Comments comments={sale.comments}/&gt; 的道具传递数据。

【讨论】:

    【解决方案3】:

    提交表单后无法手动调用函数getSale是什么原因? (没有useEffect)。

    有时useEffect 被过度使用。


    这是 Dan Abramov 的相关引述

    总而言之,如果发生某事是因为 用户 做了某事, useEffect 可能不是最好的工具。

    另一方面,如果效果只是同步某些东西(Google 将小部件上的坐标)映射到当前状态,useEffect 很好 工具。而且它可以安全地过火。

    【讨论】:

    • 非常感谢。我是 React 的新手(显然),这是一个非常简单的解决方案,也是一个很好的例子,说明你如何通过尝试做一些花哨的事情来过度思考自己的答案。
    猜你喜欢
    • 2020-07-12
    • 2021-06-23
    • 1970-01-01
    • 2021-01-06
    • 1970-01-01
    • 2021-03-05
    • 2022-01-24
    • 1970-01-01
    • 2019-01-08
    相关资源
    最近更新 更多