web-dev-qa-db-fra.com

getDerivedStateFromProps statique qui nécessite des accessoires précédents et un rappel d'état?

Je viens de mettre à jour pour réagir en natif 0.54.0 qui inclut également l'alpha 1 de react 16.3, bien sûr, je reçois beaucoup d'avertissements concernant la dépréciation de componentWillMount et componentWillReceiveProps.

J'ai un composant de route animée qui s'appuyait sur componentWillReceiveProps en son cœur, il reçoit un nouveau chemin, le compare au précédent, s'il s'agit de vieux enfants, les anime, définit de nouveaux enfants et les anime.

Voici le code en question:

componentWillReceiveProps(nextProps: Props) {
    if (nextProps.pathname !== this.props.pathname) {
      this.setState({ previousChildren: this.props.children, pointerEvents: false }, () =>
        this.animate(0)
      );
    }
  }

Maintenant, pour les questions que j'ai concernant le portage de ceci vers static getDerivedStateFromProps

1) Je n'ai plus accès à this.props, donc pas d'accès au chemin précédent, je suppose que je peux stocker ces accessoires en état, est-ce la bonne approche maintenant? Cela ressemble à la répétition de données.

2) Comme je n'ai pas accès à this je ne peux pas appeler ma fonction d'animation, qui repose sur un état. Comment puis-je contourner cela?

3) Je dois d'abord définir l'état, puis appeler l'animation, car getDerivedStateFromProps définit l'état en renvoyant les valeurs, je ne peux pas faire grand-chose par la suite, donc y a-t-il un moyen de définir l'état et après avoir exécuté un rappeler?

4) pathname bit n'est utilisé que dans componentWillreceiveProps pour le moment, si je le mets en état et n'utilise jamais this.state à l'intérieur getDerivedStateFromProps (parce que je ne peux pas) this.state.pathname les erreurs étant définies mais jamais utilisées. La meilleure approche est-elle ici pour la rendre statique également?

Mon premier réflexe a été de le changer en componentDidUpdate, mais nous ne sommes pas censés utiliser setState à l'intérieur, n'est-ce pas? (cela fonctionne cependant dans mon scénario).

  componentDidUpdate(prevProps: Props) {
    if (this.props.pathname !== prevProps.pathname) {
      this.setState({ previousChildren: prevProps.children, pointerEvents: false }, () =>
        this.animate(0)
      );
    }
  }

REMARQUE: D'après ce que j'ai entendu, je pense qu'il existe une fonctionnalité en "suspense" qui nous permettra de garder les vues montées sur l'événement de démontage de composant? Je n'ai pas pu trouver de référence à cela, mais cela ressemble à quelque chose que je pourrais potentiellement utiliser pour cette animation

Pour toute personne intéressée, il s'agit d'un extrait d'un composant complet en question

// @flow
import React, { Component, type Node } from "react";
import { Animated } from "react-native";

type Props = {
  pathname: string,
  children: Node
};

type State = {
  animation: Animated.Value,
  previousChildren: Node,
  pointerEvents: boolean
};

class OnboardingRouteAnomation extends Component<Props, State> {
  state = {
    animation: new Animated.Value(1),
    previousChildren: null,
    pointerEvents: true
  };

  componentWillReceiveProps(nextProps: Props) {
    if (nextProps.pathname !== this.props.pathname) {
      this.setState({ previousChildren: this.props.children, pointerEvents: false }, () =>
        this.animate(0)
      );
    }
  }

  animate = (value: 0 | 1) => {
    Animated.timing(this.state.animation, {
      toValue: value,
      duration: 150
    }).start(() => this.animationLogic(value));
  };

  animationLogic = (value: 0 | 1) => {
    if (value === 0) {
      this.setState({ previousChildren: null, pointerEvents: true }, () => this.animate(1));
    }
  };

  render() {
    const { animation, previousChildren, pointerEvents } = this.state;
    const { children } = this.props;
    return (
      <Animated.View
        pointerEvents={pointerEvents ? "auto" : "none"}
        style={{
          alignItems: "center",
          opacity: animation.interpolate({ inputRange: [0, 1], outputRange: [0, 1] }),
          transform: [
            {
              scale: animation.interpolate({ inputRange: [0, 1], outputRange: [0.94, 1] })
            }
          ]
        }}
      >
        {previousChildren || children}
      </Animated.View>
    );
  }
}

export default OnboardingRouteAnomation;
11
Ilja

Avec react version 16.3, l'utilisation de componentWillReceiveProps est très bien, mais provoquera l'affichage d'un avertissement de dépréciation dans la console. Il sera supprimé dans la version 17 . FWIW Dan Abramov a mis en garde contre l'utilisation de la fonctionnalité alpha dans la production - mais c'était en mars 2018.

Passons en revue vos questions:

1) Je n'ai plus accès à this.props, donc pas d'accès au chemin précédent, je suppose que je peux stocker ces accessoires en état. Est-ce la bonne approche maintenant? Cela ressemble à la répétition de données.

Oui.

Si vous souhaitez mettre à jour/restituer votre composant, vous devez combiner props et state. Dans votre cas, vous souhaitez mettre à jour l'animation composant/déclencheur lorsque pathname est modifié avec une méthode de cycle de vie.

Cela ressemble à la répétition de données.

check this out: React Team post on State and Lifecycle

2) Comme je n'y ai pas accès, je ne peux pas appeler ma fonction d'animation, qui dépend de l'état. Comment puis-je contourner cela?

tilisez componentDidUpdate

componentDidUpdate

Cette fonction sera appelée une fois le rendu terminé dans chacun des cycles de nouveau rendu. Cela signifie que vous pouvez être sûr que le composant et tous ses sous-composants se sont correctement rendus.

Cela signifie que vous pouvez appeler animate après setState dans componentDidUpdate

3) Je dois d'abord définir l'état, puis appeler l'animation, car getDerivedStateFromProps définit l'état en renvoyant les valeurs, je ne peux pas faire grand-chose après, alors y a-t-il un moyen de définir l'état et après cela, exécuter un rappel?

Se référer au point # 2 ( tiliser componentDidUpdate )

4) Le bit de chemin d'accès n'est utilisé que dans componentWillreceiveProps en ce moment, si je le déplace vers l'état et que je n'utilise jamais this.state dans getDerivedStateFromProps (parce que je ne peux pas) les erreurs this.state.pathname sont définies mais jamais utilisées. La meilleure approche est-elle ici pour la rendre statique également?

Non, il sera utilisé par componentDidUpdate

Voici comment j'aborderais cela:

// @flow
import React, { Component, type Node } from "react";

type Props = {
    pathname: string,
    children: Node
};

type State = {
    previousChildren: Node,
    pointerEvents: boolean
};

class OnboardingRouteAnomation extends Component<Props, State> {
    state = {
        previousChildren: null,
        pointerEvents: true,
        pathname: ''
    };

    static getDerivedStateFromProps(nextProps, prevState) {
        if (nextProps.pathname !== prevState.pathname) {
            return {
                previousChildren: nextProps.children,
                pointerEvents: false,
                pathname: nextProps.pathname
            };
        }

        return null;
    }

    componentDidUpdate(prevProps, prevState) {
        if (prevState.pathname !== this.state.pathname){
            console.log("prevState.pathname", prevState.pathname);
            console.log("this.props.pathname", this.props.pathname);
            this.animate(0);
        }
    }

    componentDidMount(){
        this.setState({ pathname: this.props.pathname});
    }

    animate = (value: 0 | 1) => {
        console.log("this animate called", this);
        animationLogic(value);
    };

    animationLogic = (value: 0 | 1) => {
        if (value === 0) {
            this.setState({ previousChildren: null, pointerEvents: true }, () => this.animate(1));
        }
    };

    render() {
        const { animation, previousChildren, pointerEvents } = this.state;
        const { children } = this.props;
        return (
            <div>
                {this.props.children}
            </div>
        );
    }
}

export default OnboardingRouteAnomation;

Je crois que c'est ainsi que les développeurs de Rea ont voulu que cela soit géré. Vous devriez appeler animate après la mise à jour via componentDidUpdate car c'est un effet secondaire.

J'utiliserais un nom plus descriptif pour indiquer que le chemin a changé. Quelque chose comme isPathUpdated. Vous pourriez alors avoir animate vérifier isPathUpdated comme bascule.

12
hendrathings