#javascript #reactjs #react-hooks
#javascript #reactjs #реагирующие хуки
Вопрос:
Я рендерю серию кадров из видео, каждый в своем собственном компоненте. Кадры хранятся как состояние в родительском компоненте, называемом Timeline.
Поскольку мне нужно манипулировать элементами массива фреймов из дочерних компонентов, я передаю функции обратного вызова, которые имеют индекс i
с закрытием. Чтобы избежать изменений в отдельных фреймах, влияющих на все фреймы, я использовал memo
компонент более высокого порядка вокруг каждого Frame
.
Тем не менее, я обнаружил, что каждый раз, когда я выбираю фрейм, все фреймы повторно отображаются. Использование whyDidyouUpdate
крючка (https://usehooks.com/useWhyDidYouUpdate /), я точно определил это по моему обратному вызову, изменяющемуся при каждом рендеринге.
Я попытался сделать onClick={useCallback(() => selectFrame(i), [])}
, но вместо этого фрейм просто не меняет цвета, указывая мне, что это каким-то образом помешало правильному функционированию обратного вызова.
let Frame = memo(({frame, ...}) => (
<img style={{border: frame.selected ? "green" : "red";}} />
);
let Timeline = () => {
let [frames, setFrames] = useState([
{ id: "00000", selected: True, ...more image information },
{ id: "00001", selected: True, ...more image information },
{ id: "00002", selected: True, ...more image information },
]);
let selectFrame = (i) => {
let newFrames = [...frames];
newFrames[i].mask = base64;
setFrames(newFrames);
}
return (
<div>
{frames.map((f, i) => (
<Frame
frame={f}
key={f.id}
onClick={() => selectFrame(i)}
/>
)}
</div>
));
}
Есть какие-нибудь предложения по правильному способу решения этой проблемы? Спасибо
РЕДАКТИРОВАТЬ: решение Дрю, приведенное ниже, указало мне правильное направление. Правильный синтаксис таков:
const selectFrame = (i) => useCallback(() =>
setFrames(frames.map((f, j) => (
(i === j) ? {...f, selected: !f.selected} : f
))), []);
Для каждой функции вам нужно иметь отдельный запоминаемый обратный вызов.
Комментарии:
1. Я склоняюсь к тому, что это просто правильное поведение React. Вы передаете обратный вызов, и когда он запускается, это вызывает повторный рендеринг в компонентах, в которые он был передан. Если вы хотите полностью избежать этого, вам может просто потребоваться отменить состояние / поведение, которыми вы манипулируете, с обратным вызовом к наиболее распространенному предку / родительскому
2. Потенциально, если это общее поведение с сохранением состояния (не контекстное), возможно, оно может стать пользовательским подключением…
Ответ №1:
memo
HOC просто выполняет неглубокое сравнение реквизитов, каждый раз, когда они frames
сопоставляются, создается новый onClick
обратный вызов. Преобразуйте в обратный вызов curried, который включает индекс и возвращает, и использует обновление функционального состояния, и запоминайте обратный вызов с помощью useCallback
перехвата.
const Timeline = () => {
const [frames, setFrames] = useState([
{ id: "00000", selected: True, ...more image information },
{ id: "00001", selected: True, ...more image information },
{ id: "00002", selected: True, ...more image information },
]);
const selectFrame = useCallback(index => () => {
setFrames(frames => frames.map((frame, i) => i === index ? {
...frame,
mask: base64;
} : frame));
}, []);
return (
<div>
{frames.map((f, i) => (
<Frame
frame={f}
key={f.id}
onClick={selectFrame(i)}
/>
)}
</div>
));
}
Комментарии:
1. Это сработало! Не могли бы вы подробнее рассказать о том, почему обновление состояния должно быть функциональным?
2. @CristobalSciutto Я полагаю, что это не обязательно должно быть обновление функционального состояния, но это, безусловно, шаблон, который приводит к меньшему количеству ошибок в коде. Если есть какой -либо шанс, что два или более обновления состояния могут быть поставлены в очередь за один цикл рендеринга (т. Е.
onClick
быстрый щелчок по двум кадрам), то при обычных обновлениях все поставленные в очередь обновления используют одно и то же текущее состояние по сравнению с состоянием из предыдущего вычисления обновления. Я должен был также указать, чтоnewFrames[i].mask = base64;
считается мутацией состояния, следовательно, создавая новый объект frame{ ...frame, mask: base64 }
.