#javascript #reactjs
#javascript #reactjs
Вопрос:
Я пытаюсь изучить перехватчики react. Мне нужна помощь в понимании того, всегда ли функции react сбрасывают состояние перехвата при каждом рендеринге.
Вот небольшой пример прокрутки, в котором я пытаюсь исправить заголовок
class Header extends Component {
constructor(props) {
super(props);
console.log("Constructor")
this.state = {fixed: false};
}
handleScroll = () => {
console.log(window.scrollY);
console.log(this.state.fixed);
if (window.scrollY >= 25 amp;amp; !this.state.fixed) {
this.setState({
fixed: true
});
} else if(window.scrollY < 25 amp;amp; this.state.fixed){
this.setState({
fixed: false
});
}
}
componentDidUpdate(prevProps, prevState, snapshot) {
console.log("updated ");
}
componentDidMount() {
console.log("added");
window.addEventListener('scroll', this.handleScroll);
}
componentWillUnmount() {
console.log("removed");
window.removeEventListener('scroll', this.handleScroll);
}
render() {
return (
<div></div>
);
}
}
Вывод:
Header.js:26 Constructor
Header.js:51 added
Header.js:32 1
Header.js:33 false
Header.js:32 2
Header.js:33 false
Header.js:32 5
Header.js:33 false
Header.js:32 9
Header.js:33 false
Header.js:32 15
Header.js:33 false
Header.js:32 20
Header.js:33 false
Header.js:32 26
Header.js:33 false
Header.js:47 updated
Header.js:32 31
Header.js:33 true
Header.js:32 35
Header.js:33 true
Header.js:32 38
Header.js:33 true
Header.js:32 40
Header.js:33 true
Header.js:32 39
Header.js:33 true
Header.js:32 38
Header.js:33 true
Header.js:32 35
Header.js:33 true
Header.js:32 31
Header.js:33 true
Header.js:32 25
Header.js:33 true
Header.js:32 20
Header.js:33 true
Header.js:47 updated
Header.js:32 9
Header.js:33 false
Header.js:32 5
Header.js:33 false
Header.js:32 2
Header.js:33 false
Header.js:32 0
Header.js:33 false
Это работает нормально.
Теперь с реактивными крючками та же логика. Я просто пытаюсь посмотреть на поведение состояния FixedHeader.
const Header = props => {
console.log("rendering");
const [FixedHeader, setFixedHeader] = useState(false);
useEffect(() => {
document.addEventListener("scroll", () => {
console.log(window.scrollY);
console.log(FixedHeader);
if (window.scrollY >= 30) {
setFixedHeader(true);
} else {
setFixedHeader(false);
}
})
}, []);
return (
<div></div>
);
}
Вывод:
Header.js:23 rendering
Header.js:28 1
Header.js:29 false
Header.js:28 2
Header.js:29 false
Header.js:28 5
Header.js:29 false
Header.js:28 9
Header.js:29 false
Header.js:28 15
Header.js:29 false
Header.js:28 20
Header.js:29 false
Header.js:28 26
Header.js:29 false
Header.js:28 31
Header.js:29 false
Header.js:23 rendering
Header.js:28 35
Header.js:29 false
Header.js:23 rendering
Header.js:28 38
Header.js:29 false
Header.js:28 40
Header.js:29 false
Header.js:28 39
Header.js:29 false
Header.js:28 38
Header.js:29 false
Header.js:28 35
Header.js:29 false
Header.js:28 31
Header.js:29 false
Header.js:28 25
Header.js:29 false
Header.js:23 rendering
Header.js:28 20
Header.js:29 false
Header.js:23 rendering
Header.js:28 14
Header.js:29 false
Header.js:28 9
Header.js:29 false
Header.js:28 5
Header.js:29 false
Header.js:28 2
Header.js:29 false
Header.js:28 0
Header.js:29 false
Я не могу понять это поведение, почему рендеринг вызывается дважды, а значение FixedHeader равно false, даже если scroll >= 30.
Я новичок в js, поэтому я думаю, что именно так рендеринг работы функции выполняется 2 раза, потому что функция вызывалась дважды, но почему дважды, и каждый раз, когда вызывается функция, все состояния нужно устанавливать заново, не повлияет ли это на производительность (я могу ошибаться).
Примечание: я не добавил html-часть, это простой заголовок, в который я добавляю класс «fixed-top» на основе логической переменной.
Комментарии:
1. Вы переходите
[]
кuseEffect
, что означает, что код выполняется только при первом отображении компонента. В этот моментFixedHeader
имеет значениеfalse
. Каждый раз, когда компонент отображает, создается новаяFixedHeader
переменная.2. OMG вопрос о фактической производительности фактического кода вместо предположений без загрузки. 1 вам!
3. @FelixKling означает ли это более низкую производительность по сравнению с class.
4. Если вы имеете в виду, занимает ли добавление и удаление обработчика событий каждый раз при изменении значения больше времени, чем не делать этого, тогда да 😉 Я сомневаюсь, что это окажет какое-либо ощутимое влияние.
5. @FelixKling я имею в виду, что вы сказали: «Каждый раз, когда компонент отображает новую переменную FixedHeader, создается». так что, если у вас много состояний, это может быть проблемой.
Ответ №1:
Я не могу понять это поведение, почему рендеринг вызывается дважды, а значение FixedHeader равно false, даже если scroll >= 30.
Я предполагаю, что из-за закрытия функции была создана функция запоминания значения FixedHeader, которое у нее было в данный момент… вы можете создать некоторый объект вне компонента и записать FixedHeader в его свойство, если вам действительно нужно увидеть значение внутри обратного вызова события прокрутки
const holder = {}
const Header = props => {
const [FixedHeader, setFixedHeader] = useState('false');
holder.value = FixedHeader
const handleScroll = function () {
console.log(window.scrollY)
console.log(holder.value)
if (window.scrollY >= 30) {
setFixedHeader('true');
} else {
setFixedHeader('false');
}
}
useEffect(() => {
document.addEventListener("scroll", handleScroll)
return () => window.removeEventListener('scroll', handleScroll);
}, []);
return (
но из-за того, что react не выполняет повторный запуск, иногда значение будет неправильным
Комментарии:
1. «Прямо сейчас я не вижу способа справиться с этим» Либо
FixedHeader
необходимоuseEffect
добавить в список зависимостей, а прослушиватели событий необходимо правильно удалить, либо можно использовать ссылку для получения «текущего» значенияFixedHeader
(при условии, что ссылка соответствующим образом обновляется).2. Нет необходимости
holder
добавлять ifFixedHeader
в список зависимостей (теперь, когда вы все равно удаляете обработчик событий).3. @FelixKling, да, вы правы, я изменил свой ответ, используя второй способ, но первый с зависимостями кажется более правильным
4. @FelixKling, но я просто сомневаюсь, что это действительно нужно для консоли. регистрируйте каждую прокрутку, я не вижу в этом смысла, поэтому может быть каждый раз, когда отправители компонентов добавляют прослушиватель событий и удаляют, это не очень хорошее решение… но в любом случае причина ясна
5. Конечно. Я предполагаю, что это просто для тестирования, чтобы увидеть, что происходит.
Ответ №2:
Причина, по которой «рендеринг» отображается дважды, заключается в том, что вы используете разные условия.
Для компонента класса, который вы используете:
if (window.scrollY >= 25 amp;amp; !this.state.fixed) {
// ...
} else if (window.scrollY < 25 amp;amp; this.state.fixed) {
// ...
}
В то время как функциональный компонент использует:
if (window.scrollY >= 30) {
// ...
} else {
// ...
}
Чтобы исправить эту проблему, вам нужно добавить проверку текущего состояния.
Однако, как вы уже заметили, проверка FixedHeader
значения всегда будет приводить к одному и тому же значению (оно не обновляется). Итак, сначала нам нужно решить эту проблему.
Проблема в том, что setFixedHeader
не обновляется FixedHeader
в текущем контексте. Он сообщает React о повторном рендеринге, используя переданное значение как новое FixedHeader
при следующем Header
вызове, но FixedHeader
в текущем контексте оно никогда не изменяется.
useEffect
позволяет возвращать функцию, которая обрабатывает очистку. Эта функция запускается, если компонент отключается или перед следующим вызовом useEffect
(когда список зависимостей изменился). Добавление FixedHeader
в список зависимостей приведет к удалению предыдущего обработчика событий прокрутки (с использованием возвращаемой функции очистки) и добавлению нового обработчика событий прокрутки с использованием нового FixedHeader
значения при изменении FixedHeader
значения.
const {useEffect, useState} = React;
const Header = props => {
console.log("rendering");
const [fixed, setFixed] = useState(false);
useEffect(() => {
const handleScroll = () => {
console.log(window.scrollY);
console.log(fixed);
if (window.scrollY >= 30 amp;amp; !fixed) {
setFixed(true);
} else if (window.scrollY < 30 amp;amp; fixed) {
setFixed(false);
}
};
document.addEventListener("scroll", handleScroll);
return () => document.removeEventListener("scroll", handleScroll);
}, [fixed]);
return null;
}
ReactDOM.render(<Header />, document.querySelector("#header-container"));
body { height: 1000px; }
<script src="https://unpkg.com/react@16/umd/react.production.min.js"></script>
<script src="https://unpkg.com/react-dom@16/umd/react-dom.production.min.js"></script>
<div id="header-container"></div>
Комментарии:
1. Как сказал @FelixKling: «Каждый раз, когда компонент отображает новую переменную FixedHeader, создается». итак, я хочу знать, должен ли я придерживаться классов или изменить свой код на перехватчики react. (если критерием является производительность).
2. @anoop Да, каждый раз, когда компонент отображает, создаются новые переменные, но срок их службы также короче. Для простой логики функциональные компоненты часто выглядят чище. Я бы не стал слишком беспокоиться о производительности, пока это не станет проблемой. Предварительная оптимизация иногда может привести к тому, что вам будет сложнее понять код, что, в свою очередь, увеличивает время разработки.