I'm have a very frustrating time trying to optimize my React-Redux application.
I have a header component that is reloading on every change to the redux store. My header component is a PureComponent
I have installed why-did-you-update, and it tells me:
Header.props: Value did not change. Avoidable re-render!
This is my Component:
export class Header extends PureComponent {
logout() {
// this.props.logout();
}
signup = () => {
this.props.history.push(urls.SIGNUP)
}
render() {
console.log("=============== RELOADING HEADER")
return (
<div>
<HeaderContent
logout={this.logout.bind(this)}
signup={this.signup.bind(this)}
user={this.props.user}/>
</div>
)
}
}
export function mapStateToProps(store) {
// EDITTED: I initially thought this was causing the problem
// but i get the same issue when returning a javascript object
//const u = loginUserFactory(store);
const u ={}
return {
user: u,
}
}
export function mapDispatchToProps(dispatch) {
return {
logout: function l() {
dispatch(authActions.logout())
}
}
}
export function mergeProps(propsFromState,propsFromDispatch,ownProps) {
return {
// logout: function logout() {
// propsFromDispatch.logout()
// },
...propsFromState,
...ownProps
}
}
let HeaderContainer = connect(
mapStateToProps,
mapDispatchToProps,
mergeProps,
{pure: true}
)(Header)
export default withRouter(HeaderContainer);
Header.propTypes = {
history: PropTypes.object.isRequired,
user: PropTypes.object.isRequired,
logout: PropTypes.func.isRequired,
}
I have verified that the console.log indicating that the render function is called prints every time the redux store is changed.
If I uncomment the function in merge props whyDidYouUpdate complains that the function caused the re-render.
The re-renders are significantly impacting the performance of my app. I considered writing my own shouldComponentUpdate() function, but read that it is a bad idea to do deep equals in that function for performance reasons.
So what do I do?
EDIT:
This is the code in Login User Factory. Initially I thought this was the problem, but when I remove that code I still get the same issue.
const loginUserFactory = state => {
const u = getLoginUser(state);
const isLoggedIn = !_.isEmpty(u);
const location = getLocation(state);
return {
get id() { return u.id },
get groupNames() { return u.group_names },
get avatarSmall() { return u.avatar_small },
get name() { return u.name },
get email() { return u.email },
// my goal was to localize these methods into one file
// to avoid repeated code and
// to make potential refactoring easier
get location() { return location},
get isHost() {return u.type === "host"},
get isBooker() {return u.type === "booker"},
get isLoggedIn() { return isLoggedIn },
}
}
export default loginUserFactory;
const u = {}will create a new empty object every timemapStateToPropsis called (Which is on every change to the store).{}is not a singleton. It creates a new object. You need to store the user in your store object and get it from there. This is whymapStateToPropsgets the store as a parameter -> to actually get something from it.