Оптимизация PureComponent, подключенного к React-Redux

Мне очень сложно оптимизировать свое приложение React-Redux.

У меня есть компонент заголовка, который перезагружается при каждом изменении в хранилище redux. Мой компонент заголовка - это PureComponent

Я установил почему-вы-обновление, и он говорит мне:

Header.props: Value did not change. Avoidable re-render!

Это мой компонент:

export class Header extends PureComponent {

    logout() {
        // this.props.logout();
    }

    signup = () => {
        this.props.history.push(urls.SIGNUP)
    }

    render() {
        console.info("=============== 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,
}

Я проверил, что console.info, указывающее, что функция рендеринга вызывается, распечатывается каждый раз, когда изменяется хранилище redux.

Если я раскомментирую функцию в реквизитах слияния, почемуDidYouUpdate жалуется, что функция вызвала повторный рендеринг.

Повторные отрисовки существенно влияют на производительность моего приложения. Я подумал о написании собственной функции shouldComponentUpdate (), но прочитал, что делать глубокое равенство в этой функции - плохая идея по соображениям производительности.

Так что же мне делать?

Обновлено:

Это код в Login User Factory. Сначала я думал, что это проблема, но когда я удаляю этот код, я все еще получаю ту же проблему.

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 = {} будет создавать новый пустой объект каждый раз, когда вызывается mapStateToProps (что происходит при изменении каждый в хранилище). {} не синглтон. Создает новый объект. Вам нужно сохранить пользователя в своем объекте магазина и получить его оттуда. Вот почему mapStateToProps получает хранилище в качестве параметра ->, чтобы действительно что-то от него получить.

trixn 13.04.2018 01:52
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
В JavaScript одним из самых запутанных понятий является поведение ключевого слова "this" в стрелочной и обычной функциях.
Концепция локализации и ее применение в приложениях React ⚡️
Концепция локализации и ее применение в приложениях React ⚡️
Локализация - это процесс адаптации приложения к различным языкам и культурным требованиям. Это позволяет пользователям получить опыт, соответствующий...
Улучшение производительности загрузки с помощью Google Tag Manager и атрибута Defer
Улучшение производительности загрузки с помощью Google Tag Manager и атрибута Defer
В настоящее время производительность загрузки веб-сайта имеет решающее значение не только для удобства пользователей, но и для ранжирования в...
Безумие обратных вызовов в javascript [JS]
Безумие обратных вызовов в javascript [JS]
Здравствуйте! Юный падаван 🚀. Присоединяйся ко мне, чтобы разобраться в одной из самых запутанных концепций, когда вы начинаете изучать мир...
Система управления парковками с использованием HTML, CSS и JavaScript
Система управления парковками с использованием HTML, CSS и JavaScript
Веб-сайт по управлению парковками был создан с использованием HTML, CSS и JavaScript. Это простой сайт, ничего вычурного. Основная цель -...
JavaScript Вопросы с множественным выбором и ответы
JavaScript Вопросы с множественным выбором и ответы
Если вы ищете платформу, которая предоставляет вам бесплатный тест JavaScript MCQ (Multiple Choice Questions With Answers) для оценки ваших знаний,...
0
1
427
2
Перейти к ответу Данный вопрос помечен как решенный

Ответы 2

Ответ принят как подходящий

Я предполагаю, что loginUserFactory() создает новый пользовательский объект каждый раз, когда он вызывается, что происходит каждый раз, когда хранилище обновляется, таким образом всегда передавая новый пользовательский объект вашему компоненту, который не равен предыдущему.

Также ваш Header ничего не делает с пользователем, кроме как передает его дальше по дереву. Вместо этого вы должны подключить компонент HeaderContent и сопоставить с ним только те свойства пользовательского объекта, которые ему действительно нужны, например name.

В целом mapStateToProps() никогда не должен иметь побочных эффектов. Он должен только фильтровать / сортировать / вычислять реквизиты для подключенного компонента с учетом состояния и собственных свойств. В самых тривиальных случаях он не делает ничего, кроме возврата подмножества свойств из магазина.

Есть ли способ создать пользовательский объект, который не будет вызывать обновления каждый раз?

user1795370 12.04.2018 23:53

@ user1795370 Я думаю, что лучшим решением было бы вообще не создавать пользовательский объект каждый раз, когда вы обновляете что-либо в магазине. Это нужно сделать один раз, когда пользователь войдет в систему, а затем останется тот же объект.

trixn 12.04.2018 23:55

@ user1795370 Не могли бы вы предоставить код для loginUserFactory(), чтобы мы могли увидеть, что он делает?

trixn 12.04.2018 23:58

Я понимаю, почему то, что я делаю, неправильно. Есть некоторые случаи, когда действительно полезно иметь объект, потому что есть методы, связанные с объектом, которые я хотел бы сохранить в одном файле. Есть ли лучший способ сделать это?

user1795370 13.04.2018 00:11

@ user1795370 Да, этот метод возвращает новый объект пользователя. Вы можете избежать этого, создав этот объект только один раз, когда пользователь войдет в систему, и отобразит только этот объект из вашего магазина. Вообще говоря, mapStateToProps никогда не должен иметь побочных эффектов.

trixn 13.04.2018 00:12

@ user1795370 Я не понял этого. Ваш магазин должен быть местом для хранения пользовательских данных. Также я не совсем понимаю, почему вы создаете только геттеры, а не просто свойства.

trixn 13.04.2018 00:16

до того, как я начал использовать этот подход, я напрямую сохранял результаты вызовов API в магазине. API еще нестабилен, поэтому иногда имена полей меняются. Если это произошло, мне пришлось бы редактировать имена полей по всему коду, а не в одном файле, и это проблема, которую я пытался исправить.

user1795370 13.04.2018 00:33

@ user1795370 Понятно, думаю, можно было бы сохранить минимальные изменения. Все, что вам нужно, это одно действие, которое сопоставляет результаты вызова api с нужными свойствами для структуры вашего магазина. Это, конечно, работает только в том случае, если структура вашего магазина уже достаточно ясна.

trixn 13.04.2018 00:40

Вы используете bind в своих обработчиках кликов. Большое нет-нет! Каждый повторный рендеринг будет создавать совершенно новый экземпляр функции, когда вы выполняете привязку внутри обработчиков. Либо bind в constructor, либо превратите методы обработчика кликов в функции стрелок.

handleClick = () => {
}

// or

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

Также не выполняйте никаких манипуляций или алгоритмов в mapStateToProps или mapDispatchToProps. Они также запускают повторные рендеры. Поместите эту логику в другое место.

Хорошо, я изменил это в своем коде, но у меня все еще та же проблема

user1795370 13.04.2018 00:47

Вы удалили логику из mapStateToProps и ограничили свои обработчики кликов, чтобы они не создавали новые экземпляры самих себя, но по-прежнему имеют проблемы с производительностью? Проверьте график пламени и посмотрите, не возникли ли у вас проблемы в другом месте. marmelab.com/blog/2017/02/06/react-is-slow-react-is-fast.htm‌ l

Andrew 13.04.2018 00:52

Другие вопросы по теме