Я пытаюсь отделить презентационный компонент от компонента контейнера. У меня есть SitesTable
и SitesTableContainer
. Контейнер отвечает за запуск действий redux для получения соответствующих сайтов на основе текущего пользователя.
Проблема в том, что текущий пользователь выбирается асинхронно после того, как компонент контейнера изначально визуализирован. Это означает, что компонент-контейнер не знает, что ему нужно повторно выполнить код в своей 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);
reactjs
redux
react-redux
Дэвид
источник
источник
componentDidMount
изменитsites
узел в состоянии приложения, которое передается вSitesTable
. Узел SitesStablesites
будет изменен .Ответы:
Вы должны добавить условие в свой
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's 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)) } }
Использование хуков (React 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
.источник
user
изменении? Насколько это дорого?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()
компонент будет бесконечно повторно отображатьисточник
componentWillReceiveProps
устарел и не рекомендуется использовать.Вы можете использовать
KEY
уникальный ключ (комбинацию данных), который изменяется с помощью свойств, и этот компонент будет повторно отображен с обновленными свойствами.источник
componentWillReceiveProps(nextProps) { // your code here}
Думаю, это то мероприятие, которое вам нужно.
componentWillReceiveProps
срабатывает всякий раз, когда ваш компонент получает что-то через props. Оттуда вы можете проверить, а затем делать все, что хотите.источник
componentWillReceiveProps
устарело *Я бы порекомендовал взглянуть на этот мой ответ и посмотреть, имеет ли он отношение к тому, что вы делаете. Если я понимаю вашу настоящую проблему, это то, что вы просто неправильно используете свое асинхронное действие и не обновляете «хранилище» 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, но это поможет вам разобраться в подобных сценариях и написать соответствующий код.
источник
makeASandwichWithSecretSauce
в своем компоненте?Дружественный метод использования следующий: после обновления пропа он автоматически перерисовывает компонент:
render { let textWhenComponentUpdate = this.props.text return ( <View> <Text>{textWhenComponentUpdate}</Text> </View> ) }
источник