Я пытаюсь отсортировать массив и сразу отразить результат сортировки с помощью хука useState.
Я уже новичок, что реакция обнаруживает изменение своего состояния с помощью 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 не перерисовал страницу, поэтому эта сортировка не была отражена в пользовательском интерфейсе.
Я уже искал решения, и казалось, что многие могут решить проблему, распространив массив до вызова useState, как это объясняет переполнение стека: Почему useState не запускает повторный рендеринг?.
Однако, с моей стороны, это не работает .. Любая помощь будет очень признательна. Спасибо!
[Добавлен] И моя часть рендеринга, как показано ниже;
<>
{
sortedReviews
.map((review: IReview) => (
<ReviewBlock id = {review.id}
review = {review}
targetBook = {book}
setTargetBook = {setBook}/>
))
}
</>
Привет, miraj, я думал, что это не было повторно отрисовано, потому что порядок не был отсортирован как массив sortedReviews . Например, когда я отсортировал sortedReviews на основе CreatedDate, он не был упорядочен таким образом на странице, в то время как значение переменной было. Я понимаю?
вы помещаете sortRule в массив зависимостей. убедитесь, что он не меняется при каждом повторном рендеринге. в таком случае ваш компонент зациклится.
другое дело, setReviews будет вызываться асинхронно. он может установить данное значение до завершения вашей функции sort.
но я подтвердил, что sortedReviews был отсортирован, как ожидалось, поэтому setReviews должен получить идеально упорядоченные массивы, поэтому сортировка должна выполняться в useEffect, как и ожидалось.
Японял твою точку зрения. проблема не в sortReviews. setReviews получает reviews, когда вы инициализировали sortReviews с его помощью.
Спасибо за дальнейшую проверку. Я пробовал, как const sortedReviews =[...reviews].sort....., сделать sortedReviews из клонированного массива, а не из reviews. Однако я все еще испытываю ту же проблему ...
ой! пропустил .
reviews отсутствует в вашем списке зависимостей.
Я не думаю, что нам нужно помещать reviews в список зависимостей useEffect, так как он должен обновляться только тогда, когда будет обновлен sortRule. Я проверил, что useEffect работает должным образом при сортировке (т.е. reviews имеет правильно отсортированные элементы). Моя проблема в том, что эта сортировка на reviews не отображается на странице, поскольку useState не запускает повторный рендеринг.





Когда компоненты не перерисовываются, это почти всегда происходит из-за мутаций состояния. Здесь вы вызываете операцию мутации .sort на reviews. Вам нужно будет распространить массив перед, вы его измените.
useEffect(() => {
const sortedReviews = [...reviews].sort((review1: IReview, review2: IReview) =>
sortBy(review1, review2, sortRule)
);
setReviews(sortedReviews);
}, [sortRule]);
Но здесь есть и другие проблемы. reviews не является зависимостью от useEffect, поэтому мы хотели бы использовать обратный вызов setReviews, например setReviews(current => [...current].sort(....
В целом отсортированные данные имеют больше смысла для useMemo, чем для useState. sortRule - это состояние, а sortedReviews - производные от него.
То, как вы вызываете sortBy в качестве функции сравнения, кажется немного странным. Может это просто сбивающее с толку имя?
Также вам не нужно включать тип IReview в обратный вызов, если book правильно набран как {reviews: 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]);
...
}
Ссылка на игровую площадку с машинописным текстом
Привет, Линда, большое спасибо за проверку.
Привет, Линда, большое спасибо за проверку. SortRule на самом деле является перечислением, а значение по умолчанию похоже на (0); enum SortRule { like, new }
Я пробовал, как вы любезно показали, и он почти работает, но часть рендеринга все еще не работает. Я подтвердил, что sortedReviews обрабатывается должным образом при переключении селектора, но после переключения SortRule на новый с подобного, пользовательский интерфейс по-прежнему показывает порядок по умолчанию; на основе Like. Я обновил приведенный выше вопрос по описанию, чтобы добавить часть рендеринга, относящуюся к sortedReviews. Могу я узнать ваше мнение, может ли эта часть быть причиной проблемы?
Вы, вероятно, захотите добавить key = {review.id} в <ReviewBlock />, но я не думаю, что это ваша проблема, и я не вижу ничего плохого в рендере.
Иногда я тоже сталкиваюсь с этой проблемой, в моем случае я просто "принудительно" вызываю обратный вызов рендеринга.
Первое решение:
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 как массив u
Надеюсь, ты сможешь решить эту проблему!
Вы сделали мой день!!!! теперь он отображается по мере сортировки массива. Я понятия не имел об этой уловке. Большое спасибо!
Я ценю его! Спасибо! P.S: Я добавил другое решение, вы можете попробовать использовать в будущем xD
что сделать так, чтобы ваш компонент не подвергался повторному рендерингу?