Я прочитал миллиарды статьи о отрисовках и композиции в React, иногда с противоречивой информацией . В какой-то момент я подумал, что понял это, когда прочитал, что если React «все еще имеет ту же дочернюю поддержку, которую он получил из приложения в прошлый раз, React не посещает это поддерево».
В приведенном ниже коде я запускаю повторный рендеринг App
каждые 100 мс, устанавливая состояние, а React выводит «перерендеринг» в консоли каждые 100 мс.
Почему Child
перерисовывается при каждом коммите? Разве я не «поднимаю контент» здесь?
import { useState, useEffect } from 'react';
const App = () => {
const [now, setNow] = useState()
// Start a timer
useEffect(() => {
const interval = setInterval(() =>
setNow(Date.now()), 100)
return () => clearInterval(interval)
}, [])
return (
<div className="App">
<Parent>
<Child />
</Parent>
</div>
)
}
export default App
const Parent = ({ children }) => {
return (
<div id='parent'>
{children}
</div>
)
}
const Child = () => {
console.log('rendered')
return (
<p>whatever</p>
)
}
const { useEffect, useState } = React;
const Child = () => {
console.log('rendered')
return (
<p>whatever</p>
)
}
const Parent = ({ children }) => {
return (
<div id='parent'>
{children}
</div>
)
}
const App = () => {
const [now, setNow] = useState()
// Start a timer
useEffect(() => {
const interval = setInterval(() =>
setNow(Date.now()), 100)
return () => clearInterval(interval)
}, [])
return (
<div className="App">
<Parent>
<Child />
</Parent>
</div>
)
}
ReactDOM
.createRoot(document.getElementById("root"))
.render(<App />);
<div id="root"></div>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/18.2.0/umd/react.development.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/18.2.0/umd/react-dom.development.js"></script>
2 ответа
Вы можете запомнить часть Parent
-Child
внутри App
. Поскольку состояние App
обновляется каждые 100 мс, оно будет повторно отображаться каждые 100 мс сверху вниз. Если у вас есть элементы, которым не требуется значение now
в качестве реквизита, вам нужно будет использовать мемоизацию.
Примечание. Я также переместил логику вашего таймера в хук.
const { useEffect, useMemo, useState } = React;
const Child = () => {
console.log('rendered') // only logged one time
return (
<p>whatever</p>
)
}
const Parent = ({ children }) => {
return (
<div>
{children}
</div>
)
}
const useTime = (updateRateMs) => {
const [intervalId, setIntervalId] = useState()
const [now, setNow] = useState(Date.now())
useEffect(() => {
setIntervalId(setInterval(() => setNow(Date.now()), updateRateMs))
}, [])
useEffect(() => {
return () => {
clearTimeout(intervalId)
}
}, [intervalId])
return { now }
}
const App = () => {
const { now } = useTime(100); // Update now every 100ms
const memoizedChild = useMemo(() => (
<Parent>
<Child />
</Parent>
), [])
return (
<div className="App">
{memoizedChild}
<div>Current Time: {new Date(now).toLocaleString()}</div>
</div>
)
}
ReactDOM
.createRoot(document.getElementById("root"))
.render(<App />)
<div id="root"></div>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/18.2.0/umd/react.development.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/18.2.0/umd/react-dom.development.js"></script>
Это происходит потому, что вы создаете новый компонент <Child />
каждый раз, когда обновляется состояние в компоненте <App/>
. Таким образом, утверждение о том, что «если React все еще имеет ту же дочернюю поддержку, которую он получил от приложения в прошлый раз, React не посещает это поддерево», больше не соответствует действительности. Если вы переместите свой таймер из компонента <App/>
в компонент <Parent/>
, вы увидите в приведенном ниже фрагменте, что <Child/>
больше не будет повторно отображаться, поскольку он создается только один раз в <App/>
и передается как детская опора.
const { useEffect, useState } = React;
const Child = () => {
console.log('rendered')
return (
<p>whatever</p>
)
}
const Parent = ({ children }) => {
const [now, setNow] = useState()
// Start a timer
useEffect(() => {
const interval = setInterval(() =>
setNow(Date.now()), 100)
return () => clearInterval(interval)
}, [])
return (
<div id='parent'>
{children}
</div>
)
}
const App = () => {
return (
<div className="App">
<Parent>
<Child />
</Parent>
</div>
)
}
ReactDOM
.createRoot(document.getElementById("root"))
.render(<App />);
<div id="root"></div>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/18.2.0/umd/react.development.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/18.2.0/umd/react-dom.development.js"></script>
Похожие вопросы
Новые вопросы
javascript
По вопросам программирования на ECMAScript (JavaScript/JS) и его различных диалектах/реализациях (кроме ActionScript). Обратите внимание, что JavaScript — это НЕ Java. Включите все теги, относящиеся к вашему вопросу: например, [node.js], [jQuery], [JSON], [ReactJS], [angular], [ember.js], [vue.js], [typescript], [стройный] и т. д.