Мне очень сложно оптимизировать свое приложение 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;



![Безумие обратных вызовов в javascript [JS]](https://i.imgur.com/WsjO6zJb.png)


Я предполагаю, что loginUserFactory() создает новый пользовательский объект каждый раз, когда он вызывается, что происходит каждый раз, когда хранилище обновляется, таким образом всегда передавая новый пользовательский объект вашему компоненту, который не равен предыдущему.
Также ваш Header ничего не делает с пользователем, кроме как передает его дальше по дереву. Вместо этого вы должны подключить компонент HeaderContent и сопоставить с ним только те свойства пользовательского объекта, которые ему действительно нужны, например name.
В целом mapStateToProps() никогда не должен иметь побочных эффектов. Он должен только фильтровать / сортировать / вычислять реквизиты для подключенного компонента с учетом состояния и собственных свойств. В самых тривиальных случаях он не делает ничего, кроме возврата подмножества свойств из магазина.
Есть ли способ создать пользовательский объект, который не будет вызывать обновления каждый раз?
@ user1795370 Я думаю, что лучшим решением было бы вообще не создавать пользовательский объект каждый раз, когда вы обновляете что-либо в магазине. Это нужно сделать один раз, когда пользователь войдет в систему, а затем останется тот же объект.
@ user1795370 Не могли бы вы предоставить код для loginUserFactory(), чтобы мы могли увидеть, что он делает?
Я понимаю, почему то, что я делаю, неправильно. Есть некоторые случаи, когда действительно полезно иметь объект, потому что есть методы, связанные с объектом, которые я хотел бы сохранить в одном файле. Есть ли лучший способ сделать это?
@ user1795370 Да, этот метод возвращает новый объект пользователя. Вы можете избежать этого, создав этот объект только один раз, когда пользователь войдет в систему, и отобразит только этот объект из вашего магазина. Вообще говоря, mapStateToProps никогда не должен иметь побочных эффектов.
@ user1795370 Я не понял этого. Ваш магазин должен быть местом для хранения пользовательских данных. Также я не совсем понимаю, почему вы создаете только геттеры, а не просто свойства.
до того, как я начал использовать этот подход, я напрямую сохранял результаты вызовов API в магазине. API еще нестабилен, поэтому иногда имена полей меняются. Если это произошло, мне пришлось бы редактировать имена полей по всему коду, а не в одном файле, и это проблема, которую я пытался исправить.
@ user1795370 Понятно, думаю, можно было бы сохранить минимальные изменения. Все, что вам нужно, это одно действие, которое сопоставляет результаты вызова api с нужными свойствами для структуры вашего магазина. Это, конечно, работает только в том случае, если структура вашего магазина уже достаточно ясна.
Вы используете bind в своих обработчиках кликов. Большое нет-нет! Каждый повторный рендеринг будет создавать совершенно новый экземпляр функции, когда вы выполняете привязку внутри обработчиков. Либо bind в constructor, либо превратите методы обработчика кликов в функции стрелок.
handleClick = () => {
}
// or
constructor() {
super()
this.handleClick = this.handleClick.bind(this)
}
Также не выполняйте никаких манипуляций или алгоритмов в mapStateToProps или mapDispatchToProps. Они также запускают повторные рендеры. Поместите эту логику в другое место.
Хорошо, я изменил это в своем коде, но у меня все еще та же проблема
Вы удалили логику из mapStateToProps и ограничили свои обработчики кликов, чтобы они не создавали новые экземпляры самих себя, но по-прежнему имеют проблемы с производительностью? Проверьте график пламени и посмотрите, не возникли ли у вас проблемы в другом месте. marmelab.com/blog/2017/02/06/react-is-slow-react-is-fast.htm l
Вы все еще создаете новый пользовательский объект.
const u = {}будет создавать новый пустой объект каждый раз, когда вызываетсяmapStateToProps(что происходит при изменении каждый в хранилище).{}не синглтон. Создает новый объект. Вам нужно сохранить пользователя в своем объекте магазина и получить его оттуда. Вот почемуmapStateToPropsполучает хранилище в качестве параметра ->, чтобы действительно что-то от него получить.