React useState 在通过传播引用克隆数组时不会重新渲染

React useState is not re-rendering while it is referring cloned array by spreading

我正在尝试对数组进行排序并通过 useState 挂钩立即反映其排序结果。 我已经知道 React 正在通过 Object.is() 检测其状态变化,因此在使用 useState 之前尝试传播数组,如下所示;

const [reviews, setReviews] = useState([...book.reviews]);

    useEffect(() => {
        const sortedReviews = [...reviews]
            .sort((review1: IReview, review2: IReview) =>
                sortBy(review1, review2, sortRule));

        setReviews([...sortedReviews])
        }, [sortRule])

排序后,我检查了变量 sortedReviews 的值,它按预期排序,但 React 没有重新渲染页面,所以这个排序没有反映到 UI.

我已经搜索了解决方案,似乎很多人都可以通过在调用 useState 之前将数组传播到来解决问题,就像这个堆栈溢出解释的那样:.
但是,在我这边它不起作用。任何帮助将不胜感激。谢谢!

[已添加] 我的渲染部分如下;

    <>
        {
            sortedReviews
                .map((review: IReview) => (
                    <ReviewBlock id={review.id}
                                 review={review}
                                 targetBook={book}
                                 setTargetBook={setBook}/>
                ))
        }
    </>

当组件不重新渲染时,几乎总是由于状态突变。在这里,您在 reviews 上调用变异操作 .sort。在你改变它之前,你需要传播数组

useEffect(() => {
  const sortedReviews = [...reviews].sort((review1: IReview, review2: IReview) =>
    sortBy(review1, review2, sortRule)
  );

  setReviews(sortedReviews);
}, [sortRule]);

但是这里还有其他问题。 reviews 不是 useEffect 的依赖项,因此我们希望使用 setReviews 回调,例如 setReviews(current => [...current].sort(....

一般来说,排序数据作为 useMemouseState 更有意义。 sortRule 是一个状态,sortedReviews 是从它派生的。

您调用 sortBy 作为比较函数的方式感觉有点不对劲。也许这只是一个令人困惑的名字?

此外,如果 book 被正确输入为 {reviews: IReview[]},则您不需要在回调中包含类型 IReview

如果您包含 sortBy 函数和 sortRule 变量,那么我可以提供更多帮助。但这是我想出的。

import React, { useState, useMemo } from "react";

type IReview = {
    rating: number;
}

type Book = {
    reviews: IReview[]
}

type SortRule = string; // just a placeholder - what is this really?

declare function sortBy(a: IReview, b: IReview, rule: SortRule): number;

const MyComponent = ({book}: {book: Book}) => {
    const [sortRule, setSortRule] = useState("");

    const reviews = book.reviews;

    const sortedReviews = useMemo( () => {
        return [...reviews].sort((review1, review2) =>
            sortBy(review1, review2, sortRule)
        );
    }, [sortRule, reviews]);
...
}

Typescript Playground Link

有时我也遇到这个问题,在我的情况下,我只是“强制”渲染调用回调。

第一个解法:

const [reviews, setReviews] = useState([...book.reviews]);

    useEffect(() => {
        const sortedReviews = [...reviews]
            .sort((review1: IReview, review2: IReview) =>
                sortBy(review1, review2, sortRule));

        setReviews(()=> [...sortedReviews]) // <-- Here
        }, [sortRule])

第二种解法: 可以使用useRef实时获取数据,见下:

    const [reviews, setReviews] = useState([...book.reviews]);
    const reviewsRef = useRef();
     
            useEffect(() => {
                const sortedReviews = [...reviews]
                    .sort((review1: IReview, review2: IReview) =>
                        sortBy(review1, review2, sortRule));
        
                setReviewRef([...sortedReviews]) 
                }, [sortRule])

function setReviewRef(data){
       setReview(data);
       reviewsRef.current = data;
    }  

所以,改为使用状态 评论 使用 reviewsRef.current 作为数组

希望你能解决这个问题!