简体   繁体   English

优化React-Redux连接的PureComponent

[英]Optimizing React-Redux connected PureComponent

I'm have a very frustrating time trying to optimize my React-Redux application. 我在优化我的React-Redux应用程序方面非常沮丧。

I have a header component that is reloading on every change to the redux store. 我有一个标头组件,它在每次对redux存储的更改时都会重新加载。 My header component is a PureComponent 我的标头组件是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. 我已经验证了每次更改redux存储时都会显示console.log,该控制台指示调用了render函数。

If I uncomment the function in merge props whyDidYouUpdate complains that the function caused the re-render. 如果我在合并道具中取消注释该函数,则whyDidYouUpdate会抱怨该函数导致了重新渲染。

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. 我考虑过编写自己的shouldComponentUpdate()函数,但读到出于性能原因,对该函数进行深层相等是一个坏主意。

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;

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. 我猜loginUserFactory()每次被调用时都会创建一个新的用户对象,也就是每次商店更新时都会创建一个新的用户对象,因此总是将一个新的用户对象传递给您的组件,该对象不同于前一个对象。

Also your Header doesn't do anything with the user except passing it further down the tree. 此外, Header不会对用户执行任何操作,除非将其进一步传递到树上。 You should instead connect the HeaderContent component and only map the properties of the user object to it, that it actually needs, eg the name . 相反,您应该连接HeaderContent组件,并仅将它实际需要的用户对象的属性映射到它,例如name

In general mapStateToProps() should never have any side effects. 通常, mapStateToProps()永远不会有任何副作用。 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. 在大多数情况下,它只不过是从商店中返回属性的子集而已。

You're using bind in your click handlers. 您在点击处理程序中使用bind 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. bindconstructor或将单击处理程序方法转换为箭头函数。

handleClick = () => {
}

// or

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

Also, do not implement any manipulations or algorithms in mapStateToProps or mapDispatchToProps . 另外,请勿在mapStateToPropsmapDispatchToProps实现任何操作或算法。 Those also trigger rerenders. 那些也触发rerender。 Place that logic somewhere else. 将该逻辑放在其他地方。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM