1

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;
1
  • You are still creating a new user object. const u = {} will create a new empty object every time mapStateToProps is 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 why mapStateToProps gets the store as a parameter -> to actually get something from it. Commented Apr 12, 2018 at 23:52

2 Answers 2

4

I guess that loginUserFactory() creates a new user object every time it gets called which is every time the store gets updated thus always passing a new user object to your component that is not equal to the previous one.

Also your Header doesn't do anything with the user except passing it further down the tree. You should instead connect the HeaderContent component and only map the properties of the user object to it, that it actually needs, e.g. the name.

In general mapStateToProps() should never have any side effects. It should only filter/sort/calculate the props for the connected component given the state and the own props. In the most trivial cases it does nothing more than returning a subset of properties from the store.

Sign up to request clarification or add additional context in comments.

8 Comments

Is there a way to create a user object that won't cause an update each time?
@user1795370 I think the better solution would be to not create a user object at all every time you update anything in the store. It should be done once when the user logs in and then stay the same object.
@user1795370 Could you provide the code for loginUserFactory() so we can see what it does?
I can see why what I'm doing is wrong. There are some cases where its really helpful to have an object because there are methods associated with an object that I would like to keep in a single file. Is there a better way of doing this?
@user1795370 Yes this method returns a new user object. You can avoid this by only creating that object once when the user get logged in an that only map that object from your store. Generally speaking mapStateToProps should never have any side effects.
|
1

You're using bind in your click handlers. Big no-no! Each rerender will create a completely new instance of the function when you bind inside the handlers. Either bind in a constructor or turn your click handler methods into arrow functions.

handleClick = () => {
}

// or

constructor() {
  super()
  this.handleClick = this.handleClick.bind(this)
}

Also, do not implement any manipulations or algorithms in mapStateToProps or mapDispatchToProps. Those also trigger rerenders. Place that logic somewhere else.

2 Comments

Ok, I changed it in my code but I am still getting the same issue
You removed the logic from mapStateToProps and bound your click handlers to not make new instances of themselves but are still having performance problems? Check a flame graph and see if you're running into problems elsewhere. marmelab.com/blog/2017/02/06/react-is-slow-react-is-fast.html

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.