Reputation: 79
I have a chat based on React and Redux.
Chat container receives messageIds and using messageIds.map() I render component for each message. This component is connected to Redux, so it receives messageId and updates only if messageId was changed.
But in profiler I see a lot of (React Tree Reconciliation: Completed Root) events on new message received, which take about 6ms. How to prevent this reconcilitation or how to optimize it?
For example I have 30 messages in chat, and on each new message reconciliation takes about 6ms * 30 messages = 180 ms, even if message component was not rerendered. Below I provided screenshots of profiler.
Upvotes: 1
Views: 410
Reputation: 39270
I created an example with 3 common mistakes using react-redux connect and needless re renders:
mapStateToProps always returns a new object (state=>({val:{new:reference}})
you can prevent this by memoizing the result: ()=>{const memoizedResult=createSelector(...);return state=>memoizedResult(state)
Passing a new object as prop every time (called bad prop)
Passing a new function as callback every time (called bad callback prop)
const { Provider, connect } = ReactRedux;
const { createStore } = Redux;
const store = createStore(() =>
//reducer always returns a new object
({
val: 1,
})
);
function App({ a }) {
return (
<div>
<button onClick={a}>re render</button>
<BadContainer message="bad container" />
<GoodContainer
message="bad prop"
badProp={{ a: 22 }}
/>
<GoodContainer
message="bad callback prop"
badCallback={() => 88}
/>
<GoodContainer message="good container" />
</div>
);
}
function Message(props) {
const rendered = React.useRef(0);
rendered.current++;
return (
<div>
{props.message} rendered: {rendered.current} times
</div>
);
}
const AppContainer = connect(() => ({ a: {} }), {
a: () => ({ type: 'a' }),
})(App);
const BadContainer = connect(state =>
//bad map state, always returns new object for props
({
object: { newObject: state.val },
})
)(Message);
const GoodContainer = connect(() => {
//prepare memoized function that will only re create props
// if parameters to it changed (usually done with reselect)
const memProps = ((lastVal, lastResult) => val => {
if (lastVal !== val) {
lastResult = {
object: { newObject: val },
};
lastVal = val;
}
return lastResult;
})();
//return optimized map state to props using memoization
return state => memProps(state.val);
}, {})(Message);
ReactDOM.render(
<Provider store={store}>
<AppContainer />
</Provider>,
document.getElementById('root')
);
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/16.8.4/umd/react.production.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/16.8.4/umd/react-dom.production.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/redux/4.0.4/redux.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-redux/7.1.1/react-redux.min.js"></script>
<div id="root"></div>
Upvotes: 1