Почему при обновлении стейта родителя дочка ререндерится вместо обновления?
Не могу понять логику работы обновлений реакта.
Если вкраци описывать кейс - есть первоначальная настройка в системе, интерфейс которой я пишу.
Есть родитель с лэйаутом, который запрашивает всю инфу и делится на 2 части - 2 запроса. Это визард настройки (он идет первым запросом и содержит текущий статус настройки) и область со скроллбаром, компонент в которой рендерится по роуту. По роуту так же определяется 2й запрос, уходящий после получения ответа на состояние визарда от корня, который полученный ответ пропом прокидывает в дочку.
При обновлении стейта родителя - по инициализации всех этих запросов стейт обновляется трижды (2 запроса и одно условное обновление начального стейта в зависимости от полученных данных).
Каждый раз по каждому обновлению рендерящийся по роуту компонент маунтится по новой. Почему так происходит? Почему бы ему просто не обновиться? У меня при инициализации componentDidMount в первом компоненте пути исполняется 4 раза, КАРЛ, 4! Как с этим жить? Вернее как это исправить?
camelCaseVlad Значит я просто не понимаю как сетстейтить в реакте. Я постоянно спотыкаюсь на то что у меня каких-то хуках prev с текущим стейты с пропами одинаковы и вот эта лажа тоже говорит о кривых сетстейтах.
Компоненты у меня здоровые, я использую как правило колбэк для изменения стейта, забираю этот стейт в колбэке, как-то видоизменяю и возвращаю его заассайненую версию.
Вот, к примеру метод сета состояния визарта после запроса с сервера:
setWizardState(data) {
// выставил доп ключи, которые не нужны на сервере
Object.keys(data.steps).forEach(stepKey => {
data.steps[stepKey]['forcedOpen'] = false;
});
console.log('old state: ', Object.assign({}, this.state))
if (this.state.wizard.steps !== data.steps) {
this.setState(state => {
state.wizard.steps = data.steps;
state.wizard.active = true;
return Object.assign({}, {wizard: state.wizard})
}, () => {
console.log('new state: ', Object.assign({}, this.state))
if (this.state.wizard.steps.company.status === 'next') {
// если следующий таб в визарде next, я лочу ссылку на него
this.lockTab('company')
};
// запрос к настройкам текущего таба, которые попадут в его компонент пропсом
this.requestDataByRoute();
})
}
}
Интересно что несмотря на assign логи возвращают мне ссылки на текущий стейт.
Но блин, как тогда скопировать объект возвращаемый из setState полностью? Там же туча вложенностей.
Ну или вот к примеру, закрытие и открытие окна визарда - я возвращаю только wizard в сетстейте. Какого он мне обновил весь текущий компонент включая те что получают пропы из табов (которые пососедству с wizard)
Не углубляясь в суть вашей проблемы, может стоить подумать о рефакторинге? Код выглядит очень загнезденным, может тут и кроется ошибка.
Я стараюсь писать простой, может даже и глупый код, в какой-то степени.
Т.е. метод setWizardState у меня бы делал что то простое, типа
if (this.state.wizard.steps !== data.steps) {
this.setState(state => {
state.wizard.steps = data.steps;
state.wizard.active = true;
}
}
Далее, что зависит от этого стейта, уже калькулируется в следующей функции, и тд
Ещё интереснее, я начал сетстейтать, закидывая в функцию вместо колбэка готовый новый объект. why did you render перестал сообщать о ре-рендере, но логи компонента маркетплейса - первый таб настроек - всё равно говорят о том, что компонент ре-рендерится каждый раз при сетстейте в родителе
Сорри, начальную стейт как проп в чайлд - это антипатерн
Какие есть варианты? И странно - я не читал ничего такого когда готовился к писанине на этой упоротой либе. Ну слышал про defaultProps - я прокидываю пустые объекты и начальные значения если стейт в null или undefined и это выглядит как <Component prop={this.state.key || {}} .../>
Это правильно? Может в этом косяк?
camelCaseVlad, блин а я так делаю. Но что если ты пропами суёшь в дочу здоровенный объект, изменений в котором не избежать и эти изменения нужно вносить методами дочки? Как тогда быть? Всё равно придётся как-то из пропы при инициализации сколотить стейт
Using props to generate state in getInitialState often leads to duplication of “source of truth”, i.e. where the real data is. This is because getInitialState is only invoked when the component is first created.
можно описывать часами. Я в корне не согласен с разработчиками реакта. С Vue пропы для меня никогда не были source of truth - лишь изначальной - default value. Что если ты сам для себя в голове воспринимаешь state как единственный источник правды? У меня уже были кейсы, при которых, к примеру инпуты в модалке не работали то тех пор, пока я не сколотил из props initial state и не эмитил его из инпута. Ах да у вас в реакте нет эмиттера. Ну вы поняли о чем я. По итогу антипаттерн спас от бага, который всплыл когда я всё сделал казалось бы правильно.
Короче я буду критичен - реакт говно. Это мой первый последний проект на нём. Скорость разработки настолько медлительна и нервозна, что меня посещает научный интерес, не проще ли было описать интерфейс ванильным js...
camelCaseVlad, просто я к чему это всё - описание этого антипаттерна не говорит ни о каких технических накладках, которые могут всплыть в результате образования ссылки в state на props и тому подобное. Оно просто выразило мнение разработчиков реакта, почему вот именно они не хотят чтоб так делали. Мне глубоко похер на их мнение - мне нужно сделать работу и я делаю её так, как умею и привык. Уже битый час я ковыряю гугл чтоб понять есть ли какие-то технические накладки с такого подхода. Потому что мои common компоненты полны такой логики и без неё в некоторых местах (один я описал выше) работают криво. Возможно ли что эти ре-рендеры связаны с этим подходом