Я полагаю, что вы подключили несколько слушателей событий. Каждый раз, когда компонент Page1 выполняет повторный рендеринг, он присоединяет новый набор прослушивателей событий без очистки старых. Что вызывает повторную визуализацию? Состояние меняется. Вот почему вы видите проблему только после добавления useState
и setState
.
. Вы можете проверить это, проверив логи и заметив эту не очень полезную ошибку:
Предупреждение. Невозможно выполнить обновление состояния React для неустановленного компонента. Это неоперация, но она указывает на утечку памяти в вашем приложении. Чтобы исправить, отмените все подписки и асинхронные задачи в функции очистки useEffect. на странице 1 (в приложении. js: 13)
Вам потребуется изменить код, связанный с подключением / отключением прослушивателей событий, чтобы избежать этого. Я не знаком с Billboard, поэтому могу только сказать вам, где проблема, а не точное место для ее устранения.
Мой инстинкт говорит, что здесь Chart.jsx:
renderChart = () => {
console.log('render chart');
const { current } = this.chartRef;
if (current !== null) {
this.chartInstance = bb.generate({
...this.props.options,
bindto: this.chartRef.current
});
}
};
Обновлено с полным решением
Я был прав в том, что проблема заключается в Chart.jsx.
Слушатели должны быть подключены при создании и удалении DOM с помощью ДОМ разрушен. Вы не ошиблись, когда впервые решили использовать жизненные циклы React для этой работы, однако я считаю обратные ссылки более полезными, особенно когда некоторые из DOM могут быть уничтожены или созданы во время циклов обновления (вы нет этой проблемы).
Обратный вызов Ссылки могут быть хитрыми, не используйте функции, которые воссоздают каждый рендер (поверьте мне, это головная боль). Обратный вызов вызывается по двум причинам. Во-первых, DOM был создан и передаст вам ссылку на элемент. Во-вторых, DOM был разрушен, поэтому пришло время навести порядок. Если React обнаружит изменение в Ссылке на обратный вызов (то есть вы дадите ему новую), он скажет первой Ссылке на обратный вызов очистить, а второй Ссылке на обратный вызов для инициализации (это головная боль, о которой я говорил). Вы можете избежать этого, используя метод экземпляра.
// Bind to 'this' otherwise 'this' is lost
setChartRef = (ref) => {
// Remove listeners
if (!ref) {
console.log('no reference');
this.chartRef.current = null;
this.destroy();
}
// Add listeners
else {
console.log('new reference');
this.chartRef.current = ref;
this.createChart();
}
};
Следующая часть головоломки, которую вы хотите вызвать bb.generate
только один раз. Это привело к созданию нескольких слушателей. Поэтому я упростил и переименовал вашу renderChart
в createChart
createChart = () => {
this.chartInstance = bb.generate({
...this.props.options,
bindto: this.chartRef.current
});
};
Наконец, ни один из методов жизненного цикла не является необходимым, поскольку Ссылка на обратный вызов точно сообщает нам, когда создавать диаграмму и когда его уничтожить. Вам может быть интересно, как насчет изменения размера диаграммы? Похоже, об этом позаботятся автоматически? Я мог бы что-то упустить здесь, но если вам нужно обновить диаграмму, используйте this.chartInstance
в методе жизненного цикла обновления.
Мои полные модификации здесь: