Как компонент, связанный с сокращением, знает, когда нужно повторно отобразить?
Я, вероятно, упускаю что-то очень очевидное и хотел бы очистить себя.
Здесь мое понимание.
В наивном компоненте реакции мы имеем states
& props
. Обновление state
с помощью setState
повторно отображает весь компонент. props
в основном только для чтения, и их обновление не имеет смысла.
В компоненте реагирования, который подписывается на хранилище с избыточностью, через что-то вроде store.subscribe(render)
, он, очевидно, выполняет повторную визуализацию при каждом обновлении хранилища.
response-redux имеет помощника connect()
, который внедряет часть дерева состояний (которая представляет интерес для компонента) и actionCreators как props
в компонент, обычно через что-то вроде
const TodoListComponent = connect(
mapStateToProps,
mapDispatchToProps
)(TodoList)
Но, понимая, что setState
необходим для того, чтобы TodoListComponent
реагировал на изменение дерева состояний редукса (повторное рендеринг), я не могу найти никакого кода, связанного с state
или setState
, в TodoList
файл компонента. Это звучит примерно так:
const TodoList = ({ todos, onTodoClick }) => (
<ul>
{todos.map(todo =>
<Todo
key={todo.id}
{...todo}
onClick={() => onTodoClick(todo.id)}
/>
)}
</ul>
)
Может ли кто-нибудь указать мне правильное направление относительно того, что мне не хватает?
P.S. Я следую примеру списка задач, который поставляется в комплекте с избыточным пакетом.
Ответы
Ответ 1
Функция connect
генерирует компонент-оболочку, который подписывается на хранилище. Когда отправляется действие, вызывается обратный вызов компонента-оболочки. Затем он запускает вашу функцию mapState
и поверхностно сравнивает объект результата этого времени с объектом результата последнего времени (поэтому, если бы вы переписали поле избыточного хранилища с тем же значением, это не вызывать повторную визуализацию). Если результаты отличаются, то он передает результаты вашему "реальному" компоненту "в качестве реквизита.
Дан Абрамов написал большую упрощенную версию connect
в (connect.js), которая иллюстрирует основную идею, хотя она не показывает какую-либо работу по оптимизации. У меня также есть ссылки на ряд статей о производительности Redux, в которых обсуждаются некоторые связанные идеи.
обновление
React-Redux v6.0.0 внес некоторые важные внутренние изменения в то, как подключенные компоненты получают свои данные из хранилища.
В связи с этим я написал пост, в котором объясняется, как работает API connect
и его внутренние компоненты, и как они менялись со временем:
Идиоматический Redux: история и реализация React-Redux
Ответ 2
Мой ответ немного за пределами левого поля. Это проливает свет на проблему, которая привела меня к этому посту. В моем случае казалось, что приложение не рендерится, хотя и получило новые реквизиты.
У разработчиков React был ответ на этот часто задаваемый вопрос, который звучал так: если (хранилище) было изменено, то 99% времени, когда причина реагирует, не будет повторно отображаться. Еще ничего про остальные 1%. Мутация не была здесь.
TL;DR;
componentWillReceiveProps
- это то, как state
может синхронизироваться с новыми props
.
Пограничный случай: после обновления state
приложение снова рендерится!
Оказывается, что если ваше приложение использует только state
для отображения своих элементов, props
могут обновляться, а state
- нет, поэтому повторного рендеринга нет.
У меня было state
которое зависело от props
полученного из store
. Нужных мне данных еще не было в хранилище, поэтому я извлек их из componentDidMount
, как и положено. Я получил реквизит обратно, когда мой редуктор обновил хранилище, потому что мой компонент подключен через mapStateToProps. Но страница не визуализировалась, и состояние все еще было полно пустых строк.
Примером этого является, скажем, пользователь загрузил страницу "Редактировать пост" из сохраненного URL. У вас есть доступ к postId
из URL, но информация еще не store
, поэтому вы выбираете ее. Элементы на вашей странице являются контролируемыми компонентами, поэтому все отображаемые данные находятся в state
.
С использованием приставки данные были получены, хранилище обновлено, а компонент connect
, но приложение не отразило изменения. При ближайшем рассмотрении были получены props
, но приложение не обновилось. state
не обновлялось.
Ну, props
будет обновляться и распространяться, но state
не будет. Вы должны специально указать state
для обновления.
Вы не можете сделать это в render()
, и componentDidMount
уже завершил его циклы.
componentWillReceiveProps
- это место, где вы обновляете свойства state
которые зависят от измененного значения prop
.
Пример использования:
componentWillReceiveProps(nextProps){
if (this.props.post.category !== nextProps.post.category){
this.setState({
title: nextProps.post.title,
body: nextProps.post.body,
category: nextProps.post.category,
})
}
}
Я должен покричать на эту статью, которая прояснила мне решение, о котором не упоминались десятки других постов, блогов и репозиториев. Любой, у кого возникли проблемы с поиском ответа на эту явно неясную проблему, вот она:
Методы жизненного цикла компонентов ReactJ - глубокое погружение
componentWillReceiveProps
вы будете обновлять state
чтобы синхронизировать его с обновлениями props
.
Как только state
обновляется, тогда поля в зависимости от state
перерисовываются!
Ответ 3
поскольку я знаю только, что делает редукс, при изменении состояния хранилища вызывает компонент componentWillRecieveProps, если ваш компонент зависел от мутированного состояния, а затем вы должны заставить свой компонент обновлять его, как это
1-store State change-2-call (componentWillRecieveProps (() => {3-компонентное изменение состояния}))
Ответ 4
Этот ответ является кратким изложением статьи Брайана Вона, озаглавленной " Вероятно, вам не нужно производное состояние" (07 июня 2018 г.).
Получение состояния от реквизита является анти-паттерном во всех его формах. В том числе с использованием более старых componentWillReceiveProps
и более новых getDerivedStateFromProps
.
Вместо получения состояния из реквизита, рассмотрите следующие решения.
Две лучшие рекомендации
Рекомендация 1. Полностью контролируемый компонент function EmailInput(props) {
return <input onChange={props.onChange} value={props.email} />;
}
Рекомендация 2. Полностью неуправляемый компонент с ключом // parent class
class EmailInput extends Component {
state = { email: this.props.defaultEmail };
handleChange = event => {
this.setState({ email: event.target.value });
};
render() {
return <input onChange={this.handleChange} value={this.state.email} />;
}
}
// child instance
<EmailInput
defaultEmail={this.props.user.email}
key={this.props.user.id}
/>
Два варианта, если по какой-либо причине рекомендации не подходят для вашей ситуации.
Альтернатива 1: сбросить неконтролируемый компонент с помощью идентификатора проп class EmailInput extends Component {
state = {
email: this.props.defaultEmail,
prevPropsUserID: this.props.userID
};
static getDerivedStateFromProps(props, state) {
// Any time the current user changes,
// Reset any parts of state that are tied to that user.
// In this simple example, that just the email.
if (props.userID !== state.prevPropsUserID) {
return {
prevPropsUserID: props.userID,
email: props.defaultEmail
};
}
return null;
}
// ...
}
Альтернатива 2: сбросить неуправляемый компонент методом экземпляра class EmailInput extends Component {
state = {
email: this.props.defaultEmail
};
resetEmailForNewUser(newEmail) {
this.setState({ email: newEmail });
}
// ...
}