J'ai un composant React qui distribue un changement d'état de redux dans sa fonction componentWillMount
. La raison en est que lorsque le composant est chargé, il doit extraire la id
de l'url (avec le code react-router
) et déclencher une action qui configure l'état avec les données de cette id
.
Voici le composant:
class Editor extends React.Component {
componentWillMount() {
const { dispatch, params } = this.props
dispatch(editItem(params.id))
}
render() {
const item = this.props.item
console.log("Editing", item)
}
}
export default connect(state => ({item: state.item}))(Editor)
Voici le problème: render
se fait appeler deux fois. item
n'est pas défini au premier appel et est valide au second. Idéalement, il ne devrait être appelé que lorsque this.props.item
existe réellement (après que l'action editItem
ait été envoyée et exécutée).
Selon le React docs : "Si vous appelez setState
dans cette méthode, render()
verra l'état mis à jour et ne sera exécuté qu'une fois malgré le changement d'état."
Dans redux, dispatch
est l'équivalent d'appeler setState
, car il en résulte un changement d'état. Cependant, je suppose que quelque chose dans la façon dont fonctionne connect
provoque toujours l'appel de render
à deux reprises.
Y a-t-il un moyen de contourner ce problème en plus d'ajouter une ligne comme if (!item) return;
?
Une chose à faire est de créer un composant d'ordre supérieur qui gère le modèle de base de chargement d'un composant différent (ou d'aucun composant) avant le chargement des accessoires requis.
export const LoaderWrapper = function(hasLoaded, Component, LoaderComponent, onLoad) {
return props => {
if (hasLoaded(props)) {
return <Component {...props} />
}
else {
if (onLoad) onLoad(props)
return { LoaderComponent ? <LoaderComponent /> : null }
}
}
}
Vous pouvez ensuite envelopper votre composant avant de le connecter pour obtenir le comportement souhaité.
export default connect(state => ({item: state.item}))(LoaderWrapper(
((props) => !!props.item),
Editor,
null,
(props) => props.dispatch(editItem(props.params.id))
))
Vous voudrez peut-être ajouter de la magie du currying pour pouvoir composer plus facilement ces types de fonctions d’emballage. Jetez un coup d'oeil à recomposer pour plus d'informations.
Il semble qu'il y ait déjà un problème dans la bibliothèque react-redux.
Que fait editItem? Est-ce qu'il ajoute un élément à l'état redux ou est-il déjà là?
Si cela est ajouté, j'imagine que ce qui se passe est qu'un cycle de rendu se produit avec les accessoires actuels, c'est-à-dire que l'élément est vide .
Une solution pour résoudre ce type de problème consiste à créer un composant d'ordre supérieur englobant Editor et appelant l'action de répartition, mais le rendu est défini sur un écran de chargement ou sur un div vide jusqu'à ce que l'élément soit défini. De cette façon, vous pouvez être assuré que l'éditeur aura un élément.
Mais sans savoir ce que editItem fait, c'est un peu difficile à savoir. Peut-être pourriez-vous coller le code pour cela?