Я ожидаю перезагрузки состояния при изменении реквизита, но это не работает, и переменная user не обновляется при следующем вызове useState, что не так?
function Avatar(props) {
const [user, setUser] = React.useState({...props.user});
return user.avatar ?
(<img src = {user.avatar}/>)
: (<p>Loading...</p>);
}





Согласно Документация ReactJS о хуках :
But what happens if the friend prop changes while the component is on the screen? Our component would continue displaying the online status of a different friend. This is a bug. We would also cause a memory leak or crash when unmounting since the unsubscribe call would use the wrong friend ID.
Ваше единственное взаимодействие здесь должно происходить при смене реквизита, что, похоже, не работает. Вы можете (по-прежнему в соответствии с документом) использовать componentDidUpdate(prevProps) для упреждающего обнаружения любых обновлений реквизита.
PS: у меня недостаточно кода, чтобы судить, но не могли бы вы активно setUser() внутри вашей функции Avatar(props)?
Параметр, переданный в React.useState(), является только начальным значением для этого состояния. React не распознает это состояние как изменение, а только установит его значение по умолчанию. Сначала вы захотите установить состояние по умолчанию, а затем условно вызвать setUser(newValue), которое будет распознано как новое состояние, и выполнить повторную визуализацию компонента.
Я бы рекомендовал с осторожностью обновлять состояние без каких-либо условий, чтобы предотвратить его постоянное обновление и, следовательно, повторную визуализацию каждый раз при получении реквизита. Возможно, вы захотите рассмотреть возможность переноса функциональности состояния на родительский компонент и передачи состояния родителя этому аватару в качестве опоры.
Аргумент, передаваемый в useState, является начальным состоянием, очень похожим на настройку состояния в конструкторе для компонента класса, и не используется для обновления состояния при повторном рендеринге.
Если вы хотите обновить состояние при изменении реквизита, используйте useEffect хук
function Avatar(props) {
const [user, setUser] = React.useState({...props.user});
React.useEffect(() => {
setUser(props.user);
}, [props.user])
return user.avatar ?
(<img src = {user.avatar}/>)
: (<p>Loading...</p>);
}
Может быть, этот пост прольет на это свет stackoverflow.com/questions/54673188/…
если вам нужно объединить реквизиты в состояние, вы застрянете в бесконечном цикле при использовании приложения create-реагировать, которое требует, чтобы состояние было в массиве зависимостей
Это заканчивается установкой начального состояния дважды, что довольно раздражает. Интересно, можете ли вы установить начальное состояние равным нулю, а затем использовать React.useEffect для установки начального состояния каждый раз.
Это не работает, если состояние также управляет рендерингом. У меня есть функциональный компонент, который рендерится на основе реквизита и рендерится на основе состояния. Если состояние указывает компоненту на рендеринг, потому что он изменился, тогда запускается useEffect и возвращает состояние к значению по умолчанию. Это действительно плохой дизайн с их стороны.
Я был в том же случае, о котором упоминал @GregVeres, - я хотел создать оптимистичную кнопку переключения. Должно быть так просто, но с новыми хуками React 16.3 устарел, как мы могли сделать это чистым способом. Решение — задокументированный взлом: заставить React воссоздать ваш компонент, связав его ключ с тем, что должно измениться внутри него. Таким образом, вы гарантированно сбросите состояние при изменении соответствующих реквизитов! Не уверен, смеюсь я или плачу.
@igorsantos07 igorsantos07 Состояние возвращается к изменению значения реквизита только в том случае, если изменяется props.user, а не иначе.
@CMCDragonkai, вы можете использовать ссылку, чтобы пропустить первоначальный рендеринг после монтирования компонента: stackoverflow.com/questions/53179075/…
Почему вы используете React.useState({...props.user}) вместо просто React.useState(props.user)?
Попали в ту же ловушку бесконечного цикла обновления компонентов, когда рендеринг зависел от реквизита и состояния. Решается путем разделения компонента на внешний и внутренний компоненты. Внешний компонент вычисляет начальное состояние для внутреннего компонента только на основе внешних реквизитов. Затем начальное состояние передается внутреннему компоненту через реквизит и используется для инициализации внутреннего состояния. Обновление внутренних состояний, когда не вызывает бесконечный цикл, но изменение внешних реквизитов корректно повторно инициализирует внутренние.
Тогда в чем разница от использования ленивой версии React.useState, которая принимает лямбда?
Можете ли вы объяснить, в чем необходимость использования useState внутри компонента аватара, поскольку он зависит от переданных реквизитов и никогда не меняется внутри компонента? Почему бы не использовать напрямую props.user ? Компонент получает повторный рендеринг даже при изменении реквизита.
@AntonioPantano, вы можете напрямую использовать props.user, когда дочерний компонент временно не изменяет реквизит локально. Приведенное выше решение предназначено для демонстрации того, как вы можете справиться с этим, если есть требование, подобное этому.
Функциональные компоненты, в которых мы используем useState для установки начальных значений нашей переменной, если мы передаем начальное значение через реквизит, оно всегда будет устанавливать одно и то же начальное значение, пока вы не используете useEffect хук,
например, ваш случай, это сделает вашу работу
React.useEffect(() => {
setUser(props.user);
}, [props.user])
Функция, переданная в useEffect, будет запущена после фиксации рендеринга на экране.
По умолчанию эффекты запускаются после каждого завершенного рендеринга, но вы можете активировать их только при изменении определенных значений.
React.useEffect(FunctionYouWantToRunAfterEveryRender)
если вы передадите только один аргумент в useEffect, он будет запускать этот метод после каждого рендеринга.
вы можете решить, когда запускать этот FunctionYouWantToRunAfterEveryRender, передав второй аргумент в useEffect
React.useEffect(FunctionYouWantToRunAfterEveryRender, [props.user])
как вы заметили, я передаю [props.user] сейчас useEffect будет запускать эту FunctionYouWantToRunAfterEveryRender функцию только при изменении props.user
я надеюсь, что это поможет вашему пониманию дайте мне знать, если требуются какие-либо улучшения, спасибо
Я создал такие пользовательские хуки:
const usePrevious = value => {
const ref = React.useRef();
React.useEffect(() => {
ref.current = value;
}, [value]);
return ref.current;
}
const usePropState = datas => {
const [dataset, setDataset] = useState(datas);
const prevDatas = usePrevious(datas);
const handleChangeEventDataset = data => setDataset(data);
React.useEffect(() => {
if (!deepEqual(datas, prevDatas)) // deepEqual is my function to compare object/array using deep-equal
setDataset(datas);
}, [datas, prevDatas]);
return [
dataset,
handleChangeEventDataset
]
}
Использовать:
const [state, setState] = usePropState(props.datas);
вы можете создать свои собственные простые хуки для этого. Хуки изменили значение по умолчанию, когда оно изменилось.
https://gist.github.com/mahmut-gundogdu/193ad830be31807ee4e232a05aeec1d8
import {useEffect, useState} from 'react';
export function useStateWithDep(defaultValue: any) {
const [value, setValue] = useState(defaultValue);
useEffect(() => {
setValue(defaultValue);
}, [defaultValue]);
return [value, setValue];
}
#Пример
const [user, setUser] = useStateWithDep(props.user);
При установке начального состояния в конструкторе понятно, что конструктор вызывается один раз при создании экземпляра класса. С хуками похоже, что useState вызывается каждый раз при вызове функции и каждый раз он должен инициализировать состояние, но там происходит какая-то необъяснимая «магия».