web-dev-qa-db-fra.com

Empêchez le rendu du composant à deux reprises lors de l'utilisation de redux avec composantWillMount

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;?

29
Luke Sapan

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.

7
bryanph

Il semble qu'il y ait déjà un problème dans la bibliothèque react-redux.

https://github.com/rackt/react-redux/issues/210

2
Luke Sapan

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?

0
kristian