我已经在ReactJS 16.8.5和React-Redux 3.7.2上构建了一个应用程序。当应用程序加载应用程序挂载时,将设置初始存储,并针对Firebase实时数据库设置数据库订阅。该应用程序包含标题,Sidebar
和内容部分。我已经实现了reselect和React.memo来避免在道具更改时重新渲染,但是Sidebar
组件仍在重新渲染。使用React.memo中的React profiler API和areEqual
比较函数,我可以看到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)
[Initial render]看起来可以正常工作,直到
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
我已经在ReactJS 16.8.5和React-Redux 3.7.2上构建了一个应用程序。当应用程序加载应用程序挂载时,将设置初始存储,并针对Firebase实时数据库设置数据库订阅。 ...
不需要React.memo,因为react-redux connect将返回一个纯组件,仅当您更改传递的props或在分派的动作导致状态发生任何更改后才会重新呈现。