web-dev-qa-db-fra.com

setState () à l'intérieur de composantDidUpdate ()

J'écris un script qui déplace la liste déroulante en dessous ou au dessus des entrées en fonction de la hauteur de la liste déroulante et de la position des entrées à l'écran. Aussi, je veux définir modificateur à déroulant en fonction de sa direction. Mais utiliser setState à l'intérieur de la componentDidUpdate crée une boucle infinie (ce qui est évident)

J'ai trouvé une solution en utilisant getDOMNode et en définissant le nom de classe sur une liste déroulante directement, mais j'estime qu'il devrait exister une meilleure solution en utilisant React tools. Quelqu'un peut-il m'aider?

Voici une partie du code de travail avec getDOMNode (logique de positionnement un peu négligée pour simplifier le code)

let SearchDropdown = React.createClass({
    componentDidUpdate(params) {
        let el = this.getDOMNode();
        el.classList.remove('dropDown-top');
        if(needToMoveOnTop(el)) {
            el.top = newTopValue;
            el.right = newRightValue;
            el.classList.add('dropDown-top');
        }
    },
    render() {
        let dataFeed = this.props.dataFeed;
        return (
            <DropDown >
                {dataFeed.map((data, i) => {
                    return (<DropDownRow key={response.symbol} data={data}/>);
                })}
            </DropDown>
        );
    }
});

et voici le code avec setstate (qui crée une boucle infinie)

let SearchDropdown = React.createClass({
    getInitialState() {
        return {
            top: false
        };
    },
    componentDidUpdate(params) {
        let el = this.getDOMNode();
        if (this.state.top) {
           this.setState({top: false});
        }
        if(needToMoveOnTop(el)) {
            el.top = newTopValue;
            el.right = newRightValue;
            if (!this.state.top) {
              this.setState({top: true});
           }
        }
    },
    render() {
        let dataFeed = this.props.dataFeed;
        let class = cx({'dropDown-top' : this.state.top});
        return (
            <DropDown className={class} >
                {dataFeed.map((data, i) => {
                    return (<DropDownRow key={response.symbol} data={data}/>);
                })}
            </DropDown>
        );
    }
});
93

Vous pouvez utiliser setStateinside componentDidUpdate. Le problème est que vous créez en quelque sorte une boucle infinie car il n'y a pas de condition de rupture.

Étant donné que vous avez besoin des valeurs fournies par le navigateur une fois le composant rendu, votre approche concernant l'utilisation de componentDidUpdate est correcte. Elle nécessite simplement une meilleure gestion de la condition qui déclenche le setState .

90
damianmr

Si vous utilisez setState dans componentDidUpdate, il met à jour le composant, ce qui entraîne un appel à componentDidUpdate qui appelle ultérieurement setState, ce qui entraîne la boucle infinie. Vous devez conditionnellement appeler setState et vous assurer que la condition violant l'appel se produit éventuellement, par exemple:

componentDidUpdate: function() {
    if (condition) {
        this.setState({..})
    } else {
        //do something else
    }
}

Si vous ne faites que mettre à jour le composant en lui envoyant des accessoires (il n'est pas mis à jour par setState, à l'exception du cas dans composantDidUpdate), vous pouvez appeler setState dans componentWillReceiveProps au lieu de componentDidUpdate.

51
mickeymoon

La signature componentDidUpdate est void::componentDidUpdate(previousProps, previousState). Avec cela, vous pourrez tester quels accessoires/états sont sales et appeler setState en conséquence.

Exemple:

componentDidUpdate(previousProps, previousState) {
    if (previousProps.data !== this.props.data) {
        this.setState({/*....*/})
    }
}
46
Abdennour TOUMI

Je dirais que vous devez vérifier si l'état a déjà la même valeur que vous essayez de définir. Si c'est la même chose, il n'y a pas de raison de remettre l'état à la même valeur.

Assurez-vous de définir votre état comme ceci:

let top = newValue /*true or false*/
if(top !== this.state.top){
    this.setState({top});
}
1
gradosevic

J'ai eu un problème similaire où je dois centrer l'info-bulle. React setState dans composantDidUpdate m'a mis en boucle infinie, j'ai essayé condition cela a fonctionné. Mais j’ai trouvé que l’utilisation de in callback me donnait une solution plus simple et plus propre. Si vous utilisez la fonction inline pour le callback, vous serez confronté au problème null pour chaque mise à jour de composant. Donc, utilisez la référence de la fonction dans le rappel de référence et définissez l'état dans celui-ci, ce qui lancera la restitution

0
Sanjay