#javascript #reactjs #react-hooks #state
#javascript #reactjs #реагировать-перехваты #состояние
Вопрос:
Я хочу изменить active на true в своем объекте, в журнале консоли у меня есть обновленная версия состояния, однако я не могу обновить само состояние, я чувствую, что я делаю это неправильно. любая помощь была бы фантастической, спасибо, наверняка где-то есть похожий пост, но я ничего не смог найти.
const [components, setComponents] = useState([
{
compName: "Weather",
path: 'weather',
active: false
},
{
compName: "Tasks",
path: 'tasks',
active: false
},
])
function onActivateHandler(){
setComponents((prevComponents) => {
const copy = [...prevComponents]
let newState = {...copy[1]}
newState.active = true
console.log(newState)
return {...prevComponents}
})
}
Ответ №1:
Вам просто нужно вернуть свой клонированный, измененный объект вместе с остальными prevComponents
. На данный момент вы возвращаете только старое состояние, поэтому ничего не изменится (кроме того факта, что вы случайно возвращаете объект, а не массив).
Редактировать: сейчас выглядит не так красиво, но это безопасно.
function onActivateHandler(){
setComponents((prevComponents) => {
const copy = [...s];
const newState = {...copy.splice(1, 1)[0]}
newState.active = true;
return [...copy, newState];
})
}
Комментарии:
1. хорошо, спасибо, значит, это сработало, только каждый раз это вызывало дублирование состояния вместо замены старого состояния..
2. Верно, я забыл, что вам придется это сращивать. Просто месяц.
Ответ №2:
Я думаю, что вам просто нужно сгенерировать новую ссылку для всего массива.
Еще одна вещь, которую я заметил, это то, что вы возвращаете объект из своей setComponents()
функции. Поскольку переменная состояния components
является массивом, я думаю, вам нужно вернуть массив.
Кроме того, напишите onActivateHandler()
метод в виде функции со стрелкой, чтобы привязать его к контексту компонента, на всякий случай, обычно это то, что вы хотели бы сделать.
Я бы написал это так (я переключаю активное значение просто для демонстрации):
const myComponent = () => {
const [components, setComponents] = useState([
{
compName: "Weather",
path: "weather",
active: false,
},
{
compName: "Tasks",
path: "tasks",
active: false,
},
]);
const onActivateHandler = () => {
setComponents((prevComponents) => {
// update the array reference
const newState = [...prevComponents];
newState[1].active = !newState[1].active;
// Your state variable is an array,
// so you need to return an array
return [...prevComponents];
});
};
return (
<>
<button onClick={onActivateHandler}>Click</button>
<div>{components[1].active ? 'active' : 'inactive'}</div>
</>
)
};
Как lawrence-witt
сказано ниже, отказ от создания нового объекта внутри нового массива в некоторых случаях может привести к отказу от повторного рендеринга, но в вашем простом случае приведенный выше код работает нормально, поскольку react отслеживает ссылку на массив, а не ссылку на объекты внутри. Вы можете распространить newState[1]
на новый объект, если вам нужно, но вам нужно будет удалить старый из массива состояний.
Ваше здоровье!
Комментарии:
1. Это не такая уж хорошая идея, потому что без клонирования объекта вы сохраните его ссылку и измените состояние перед его установкой. Это часто приводит к тому, что повторный запуск не запускается.
2. Нет, конечно, я в курсе этого. Я просто понимаю, что ссылка, отслеживаемая react, является ссылкой на массив. Но я могу ошибаться. Я протестирую его и отредактирую при необходимости.
3. Опасность здесь не только в том, что React не выполнит надлежащую проверку на равенство возвращаемого состояния и повторно отобразит этот компонент, но и в том, что любые подкомпоненты, которые принимают
components[1].active
в качестве prop, будут вести себя неожиданным образом, когда это значение будет преждевременно изменено. Я бы рекомендовал разделять ссылки при изменении любого состояния React.