Я использую Redux для подписки на магазин и обновления компонента.
Это упрощенный пример без Redux. Он использует макет для подписки и отправки на.
Пожалуйста, следуйте инструкциям ниже фрагмента, чтобы воспроизвести проблему.
Редактировать : Пожалуйста, перейдите к второй демонстрационный фрагмент под Обновление для более краткого и приближенного к реальному сценарию. Вопрос не о Redux. Речь идет об идентичности функции setState в React, вызывающей повторную визуализацию при определенных обстоятельствах, даже если состояние не изменилось.
Редактировать 2 : добавлена еще более краткая демонстрация в разделе " Обновление 2".
const {useState, useEffect} = React;
let counter = 0;
const createStore = () => {
const listeners = [];
const subscribe = (fn) => {
listeners.push(fn);
return () => {
listeners.splice(listeners.indexOf(fn), 1);
};
}
const dispatch = () => {
listeners.forEach(fn => fn());
};
return {dispatch, subscribe};
};
const store = createStore();
function Test() {
const [yes, setYes] = useState('yes');
useEffect(() => {
return store.subscribe(() => {
setYes('yes');
});
}, []);
console.log(`Rendered ${++counter}`);
return (
<div>
<h1>{yes}</h1>
<button onClick={() => {
setYes(yes === 'yes' ? 'no' : 'yes');
}}>Toggle</button>
<button onClick={() => {
store.dispatch();
}}>Set to Yes</button>
</div>
);
}
ReactDOM.render(<Test />, document.getElementById('root'));
<div id="root"></div>
<script src="https://unpkg.com/react/umd/react.development.js"></script>
<script src="https://unpkg.com/react-dom/umd/react-dom.development.js"></script>
Что происходит
- ✅ Нажмите «Установить на Да». Поскольку значение
yes
уже равно «да», состояние не изменяется, поэтому компонент не отображается повторно. - ✅ Нажмите «Переключить».
yes
установлено в «нет». Состояние изменилось, поэтому компонент повторно отображается. - ✅ Нажмите «Установить на Да».
yes
установлено на «да». Состояние снова изменилось, поэтому компонент повторно отображается. - ⛔ Снова нажмите «Установить на Да». Состояние не изменилось, но компонент является по-прежнему перерисован.
- ✅ Последующие нажатия кнопки «Установить на Да» не вызывают повторную визуализацию, как ожидалось.
Что должно произойти
На шаге 4 компонент не должен повторно отображаться, так как состояние не изменяется.
Обновление
Как состояние React docs , useEffect
равно
, подходящее для многих распространенных побочных эффектов, таких как настройка подписок и обработчиков событий ...
Одним из таких вариантов использования может быть прослушивание события браузера, такого как online
и offline
.
. В этом примере мы вызываем функцию внутри useEffect
один раз, когда компонент впервые выполняет рендеринг, передав ему пустой массив []
. Функция настраивает прослушиватели событий для изменений состояния в сети.
Предположим, что в интерфейсе приложения есть кнопка для ручного переключения состояния онлайн.
Пожалуйста, следуйте инструкциям ниже фрагмента, чтобы воспроизвести проблема.
const {useState, useEffect} = React;
let counter = 0;
function Test() {
const [online, setOnline] = useState(true);
useEffect(() => {
const onOnline = () => {
setOnline(true);
};
const onOffline = () => {
setOnline(false);
};
window.addEventListener('online', onOnline);
window.addEventListener('offline', onOffline);
return () => {
window.removeEventListener('online', onOnline);
window.removeEventListener('offline', onOffline);
}
}, []);
console.log(`Rendered ${++counter}`);
return (
<div>
<h1>{online ? 'Online' : 'Offline'}</h1>
<button onClick={() => {
setOnline(!online);
}}>Toggle</button>
</div>
);
}
ReactDOM.render(<Test />, document.getElementById('root'));
<div id="root"></div>
<script src="https://unpkg.com/react/umd/react.development.js"></script>
<script src="https://unpkg.com/react-dom/umd/react-dom.development.js"></script>
Что происходит
- component Компонент сначала отображается на экране, а сообщение регистрируется в консоли.
- ✅ Нажмите «Переключить».
online
установлено на false
. Состояние изменилось, поэтому компонент повторно отображается. - ⛔ Откройте инструменты Dev и на панели «Сеть» переключитесь в «автономный режим».
online
уже был false
, таким образом, состояние не изменилось, но компонент является все еще повторно обработанным.
Что ожидается
На шаге 3 компонент не должен повторно отображаться, поскольку состояние не изменяется.
Обновление 2
const {useState, useEffect} = React;
let counterRenderComplete = 0;
let counterRenderStart = 0;
function Test() {
const [yes, setYes] = useState('yes');
console.log(`Component function called ${++counterRenderComplete}`);
useEffect(() => console.log(`Render completed ${++counterRenderStart}`));
return (
<div>
<h1>{yes ? 'yes' : 'no'}</h1>
<button onClick={() => {
setYes(!yes);
}}>Toggle</button>
<button onClick={() => {
setYes('yes');
}}>Set to Yes</button>
</div>
);
}
ReactDOM.render(<Test />, document.getElementById('root'));
<div id="root"></div>
<script src="https://unpkg.com/react/umd/react.development.js"></script>
<script src="https://unpkg.com/react-dom/umd/react-dom.development.js"></script>
Что происходит
- ✅ Нажмите «Установить на Да». Поскольку значение
yes
уже равно true
, состояние не изменяется, поэтому компонент не отображается повторно. - ✅ Нажмите «Переключить».
yes
установлено на false
. Состояние изменилось, поэтому компонент повторно отображается. - ✅ Нажмите «Установить на Да».
yes
установлено на true
. Состояние снова изменилось, поэтому компонент повторно отображается. - ⛔ Нажмите «Установить на Да» еще раз. Состояние не изменилось, несмотря на то, что компонент запускает процесс рендеринга, вызывая функцию. Тем не менее, React выручает от рендеринга где-то в середине процесса, и эффекты не вызываются.
- ✅ Последующие нажатия кнопки «Установить на Да» не вызывают повторного рендеринга (вызова функций), как ожидалось.
Вопрос
Почему компонент все еще перерисовывается? Я что-то не так делаю, или это ошибка React?