Перерисовать компонент React при смене реквизита
Я пытаюсь отделить презентационный компонент от контейнерного компонента. У меня есть SitesTable
и SitesTableContainer
. Контейнер отвечает за запуск избыточных действий для извлечения соответствующих сайтов на основе текущего пользователя.
Проблема заключается в том, что текущий пользователь выбирается асинхронно после того, как компонент контейнера был визуализирован изначально. Это означает, что компонент контейнера не знает, что ему нужно повторно выполнить код в своей функции componentDidMount
которая обновит данные для отправки в SitesTable
. Я думаю, что мне нужно повторно визуализировать компонент контейнера, когда один из его реквизит (пользователь) меняется. Как мне сделать это правильно?
class SitesTableContainer extends React.Component {
static get propTypes() {
return {
sites: React.PropTypes.object,
user: React.PropTypes.object,
isManager: React.PropTypes.boolean
}
}
componentDidMount() {
if (this.props.isManager) {
this.props.dispatch(actions.fetchAllSites())
} else {
const currentUserId = this.props.user.get('id')
this.props.dispatch(actions.fetchUsersSites(currentUserId))
}
}
render() {
return <SitesTable sites={this.props.sites}/>
}
}
function mapStateToProps(state) {
const user = userUtils.getCurrentUser(state)
return {
sites: state.get('sites'),
user,
isManager: userUtils.isManager(user)
}
}
export default connect(mapStateToProps)(SitesTableContainer);
Ответы
Ответ 1
Вы должны добавить условие в ваш метод componentDidUpdate
.
В примере используется fast-deep-equal
для сравнения объектов.
import equal from 'fast-deep-equal'
...
constructor(){
this.updateUser = this.updateUser.bind(this);
}
componentDidMount() {
this.updateUser();
}
componentDidUpdate(prevProps) {
if(!equal(this.props.user, prevProps.user)) // Check if it a new user, you can also use some unique property, like the ID (this.props.user.id !== prevProps.user.id)
{
this.updateUser();
}
}
updateUser() {
if (this.props.isManager) {
this.props.dispatch(actions.fetchAllSites())
} else {
const currentUserId = this.props.user.get('id')
this.props.dispatch(actions.fetchUsersSites(currentUserId))
}
}
Использование крючков (Реакция 16.8. 0+)
import React, { useEffect } from 'react';
const SitesTableContainer = ({
user,
isManager,
dispatch,
sites,
}) => {
useEffect(() => {
if(isManager) {
dispatch(actions.fetchAllSites())
} else {
const currentUserId = user.get('id')
dispatch(actions.fetchUsersSites(currentUserId))
}
}, [user]);
return (
return <SitesTable sites={sites}/>
)
}
Если упор, который вы сравниваете, является объектом или массивом, вы должны использовать useDeepCompareEffect
вместо useEffect
.
Ответ 2
ComponentWillReceiveProps()
в будущем будет устаревшим из-за ошибок и несоответствий. Альтернативное решение для повторного рендеринга компонента при смене реквизита заключается в использовании ComponentDidUpdate()
и ShouldComponentUpdate()
.
ComponentDidUpdate()
вызывается всякий раз, когда компонент обновляется И, если ShouldComponentUpdate()
возвращает истину (если не определено ShouldComponentUpdate()
, он возвращает true
по умолчанию).
shouldComponentUpdate(nextProps){
return nextProps.changedProp !== this.state.changedProp;
}
componentDidUpdate(props){
// Desired operations: ex setting state
}
Такое же поведение может быть достигнуто с использованием только метода ComponentDidUpdate()
путем включения в него условного оператора.
componentDidUpdate(prevProps){
if(prevProps.changedProp !== this.props.changedProp){
this.setState({
changedProp: this.props.changedProp
});
}
}
Если ShouldComponentUpdate()
установить состояние без условия или без определения ShouldComponentUpdate()
компонент будет бесконечно повторно отображать
Ответ 3
componentWillReceiveProps(nextProps) { // your code here}
Я думаю, что это то, что вам нужно. componentWillReceiveProps
срабатывает каждый раз, когда ваш компонент получает что-то через реквизит. Оттуда вы можете сделать свою проверку, а затем делать все, что хотите.
Ответ 4
Я бы порекомендовал взглянуть на этот ответ и посмотреть, соответствует ли оно тому, что вы делаете. Если я понимаю вашу реальную проблему, это означает, что вы просто не используете свое асинхронное действие правильно и обновляете "хранилище" redux, которое автоматически обновит ваш компонент с помощью новых реквизитов.
Этот раздел вашего кода:
componentDidMount() {
if (this.props.isManager) {
this.props.dispatch(actions.fetchAllSites())
} else {
const currentUserId = this.props.user.get('id')
this.props.dispatch(actions.fetchUsersSites(currentUserId))
}
}
Не следует запускать в компоненте, он должен обрабатываться после выполнения вашего первого запроса.
Взгляните на этот пример из redux-thunk:
function makeASandwichWithSecretSauce(forPerson) {
// Invert control!
// Return a function that accepts `dispatch` so we can dispatch later.
// Thunk middleware knows how to turn thunk async actions into actions.
return function (dispatch) {
return fetchSecretSauce().then(
sauce => dispatch(makeASandwich(forPerson, sauce)),
error => dispatch(apologize('The Sandwich Shop', forPerson, error))
);
};
}
Вы не обязательно должны использовать redux-thunk, но это поможет вам рассуждать о подобных сценариях и писать код для соответствия.
Ответ 5
Вы можете использовать KEY
уникальный ключ (комбинацию данных), который изменяется с помощью реквизита, и этот компонент будет перерисован с обновленными реквизитами.
Ответ 6
Дружественный метод, который можно использовать, заключается в следующем: после обновления prop он автоматически переопределяет компонент:
render {
let textWhenComponentUpdate = this.props.text
return (
<View>
<Text>{textWhenComponentUpdate}</Text>
</View>
)
}