J'essaie de séparer un composant de présentation d'un composant de conteneur. J'ai un SitesTable
et un SitesTableContainer
. Le conteneur est chargé de déclencher des actions de redux pour récupérer les sites appropriés en fonction de l'utilisateur actuel.
Le problème est que l'utilisateur actuel est extrait de manière asynchrone, une fois que le composant conteneur est rendu initialement. Cela signifie que le composant conteneur ne sait pas qu'il a besoin de réexécuter le code dans sa componentDidMount
fonction qui mettrait à jour les données à envoyer au SitesTable
. Je pense que j'ai besoin de rendre à nouveau le composant conteneur lorsque l'un de ses accessoires (utilisateur) change. Comment faire cela correctement?
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
David
la source
la source
componentDidMount
changeront lesites
nœud dans l'état de l'application, qui est passé dans leSitesTable
. Lesites
nœud de SitesStable va changer.Réponses:
Vous devez ajouter une condition dans votre
componentDidUpdate
méthode.L'exemple utilise
fast-deep-equal
pour comparer les objets.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)) } }
Utiliser des crochets (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}/> ) }
Si l'accessoire que vous comparez est un objet ou un tableau, vous devez utiliser à la
useDeepCompareEffect
place deuseEffect
.la source
user
changement? Combien ça coûte?ComponentWillReceiveProps()
va être obsolète à l'avenir en raison de bogues et d'incohérences. Une solution alternative pour re-rendre un composant sur le changement d'accessoires est d'utiliserComponentDidUpdate()
etShouldComponentUpdate()
.ComponentDidUpdate()
est appelée chaque fois que le composant est mis à jour ET siShouldComponentUpdate()
renvoie true (siShouldComponentUpdate()
n'est pas défini, il retournetrue
par défaut).shouldComponentUpdate(nextProps){ return nextProps.changedProp !== this.state.changedProp; } componentDidUpdate(props){ // Desired operations: ex setting state }
Ce même comportement peut être accompli en utilisant uniquement la
ComponentDidUpdate()
méthode en incluant l'instruction conditionnelle à l'intérieur de celle-ci.componentDidUpdate(prevProps){ if(prevProps.changedProp !== this.props.changedProp){ this.setState({ changedProp: this.props.changedProp }); } }
Si l'on tente de définir l'état sans conditionnel ou sans définir
ShouldComponentUpdate()
le composant, le rendu sera infiniment nouveaula source
componentWillReceiveProps
est sur le point d'être obsolète et est suggérée contre toute utilisation.Vous pouvez utiliser
KEY
une clé unique (combinaison des données) qui change avec les accessoires, et ce composant sera rendu avec les accessoires mis à jour.la source
componentWillReceiveProps(nextProps) { // your code here}
Je pense que c'est l'événement dont vous avez besoin.
componentWillReceiveProps
se déclenche chaque fois que votre composant reçoit quelque chose via des accessoires. À partir de là, vous pouvez avoir votre vérification puis faire ce que vous voulez.la source
componentWillReceiveProps
obsolète *Je recommanderais de jeter un œil à ma réponse et de voir si elle est pertinente par rapport à ce que vous faites. Si je comprends votre vrai problème, c'est que vous n'utilisez pas correctement votre action asynchrone et mettez à jour le redux "store", qui mettra automatiquement à jour votre composant avec ses nouveaux accessoires.
Cette section de votre code:
componentDidMount() { if (this.props.isManager) { this.props.dispatch(actions.fetchAllSites()) } else { const currentUserId = this.props.user.get('id') this.props.dispatch(actions.fetchUsersSites(currentUserId)) } }
Ne doit pas se déclencher dans un composant, il doit être traité après l'exécution de votre première requête.
Jetez un œil à cet exemple de 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)) ); }; }
Vous n'êtes pas nécessairement obligé d'utiliser redux-thunk, mais cela vous aidera à raisonner sur des scénarios comme celui-ci et à écrire le code correspondant.
la source
makeASandwichWithSecretSauce
dans votre composant?Une méthode conviviale à utiliser est la suivante, une fois que le prop est mis à jour, le composant sera automatiquement rendu:
render { let textWhenComponentUpdate = this.props.text return ( <View> <Text>{textWhenComponentUpdate}</Text> </View> ) }
la source