Я создал приложение на ReactJS 16.8.5 и React-Redux 3.7.2. Когда приложение загружает приложения, устанавливается начальное хранилище, и подписки базы данных настраиваются для базы данных Firebase Realtime. Приложение содержит заголовок, Sidebar
и раздел содержимого.
Я реализовал повторный выбор вместе с React.memo , чтобы избежать повторного рендеринга при смене реквизита, но компонента Sidebar
все еще перерисовывает. Используя React Profiler API и функцию сравнения areEqual
в React.memo, я вижу, что Sidebar
отрисовывается несколько раз, хотя реквизиты равны.
app.js
//Imports etc...
const jsx = (
<React.StrictMode>
<Provider store={store}>
<AppRouter />
</Provider>
</React.StrictMode>
)
let hasRendered = false
const renderApp = () => {
if (!hasRendered) { //make sure app only renders one time
ReactDOM.render(jsx, document.getElementById('app'))
hasRendered = true
}
}
firebase.auth().onAuthStateChanged((user) => {
if (user) {
// Set initial store and db subscriptions
renderApp()
}
})
AppRouter.js
//Imports etc...
const AppRouter = ({}) => {
//...
return (
<React.Fragment>
//uses Router instead of BrowserRouter to use our own history and not the built in one
<Router history={history}>
<div className="myApp">
<Route path="">
<Sidebar ...props />
</Route>
//More routes here...
</div>
</Router>
</React.Fragment>
)
}
//...
export default connect(mapStateToProps, mapDispatchToProps)(AppRouter)
Sidebar.js
//Imports etc...
export const Sidebar = (props) => {
const onRender = (id, phase, actualDuration, baseDuration, startTime, commitTime) => {
if (id !== 'Sidebar') { return }
console.log('onRender', phase, actualDuration)
}
return (
<Profiler id="Sidebar" onRender={onRender}>
<React.Fragment>
{/* Contents of Sidebar */}
</React.Fragment>
</Profiler>
}
const getLang = state => (state.usersettings) ? state.usersettings.language : 'en'
const getMediaSize = state => (state.route) ? state.route.mediaSize : 'large'
const getNavigation = state => state.navigation
const getMyLang = createSelector(
[getLang], (lang) => console.log('Sidebar lang val changed') || lang
)
const getMyMediaSize = createSelector(
[getMediaSize], (mediaSize) => console.log('Sidebar mediaSize val changed') || mediaSize
)
const getMyNavigation = createSelector(
[getNavigation], (navigation) => console.log('Sidebar navigation val changed') || navigation
)
const mapStateToPropsMemoized = (state) => {
return {
lang: getMyLang(state),
mediaSize: getMyMediaSize(state),
navigation: getMyNavigation(state)
}
}
const areEqual = (prevProps, nextProps) => {
const areStatesEqual = _.isEqual(prevProps, nextProps)
console.log('Sidebar areStatesEqual', areStatesEqual)
return areStatesEqual
}
export default React.memo(connect(mapStateToPropsMemoized, mapDispatchToProps)(Sidebar),areEqual)
Первоначальный рендеринг выглядит нормально вплоть до Sidebar navigation val changed
- после этого компонент повторно рендерит весь много раз - почему!?
Console output - initial render
onRender Sidebar mount 572
Sidebar mediaSize val changed
Profile Sidebar areEqual true
Sidebar navigation val changed
onRender Sidebar update 153
Sidebar navigation val changed
onRender Sidebar update 142
onRender Sidebar update 103
onRender Sidebar update 49
onRender Sidebar update 5
onRender Sidebar update 2
onRender Sidebar update 12
onRender Sidebar update 3
onRender Sidebar update 2
onRender Sidebar update 58
onRender Sidebar update 2
onRender Sidebar update 4
onRender Sidebar update 5
onRender Sidebar update 4
Последующий рендеринг не влияет ни на какую часть магазина, которая сопоставлена с реквизитами (местоположение), но компонент все еще рендеринг.
Console output - subsequent render
Profile Sidebar areEqual true
onRender Sidebar update 76
onRender Sidebar update 4
Я ожидаю, что Sidebar
будет запомнен и рендерится / рендерится только несколько раз во время монтирования / обновления хранилища во время начальной загрузки.
Почему компонент Sidebar
отображается так много раз?
С уважением / K