web-dev-qa-db-fra.com

Stockage de variables non étatiques dans des composants fonctionnels

Ci-dessous se trouvent deux React composants qui font presque la même chose. L'un est une fonction; l'autre est une classe. Chaque composant a un Animated.Value avec un écouteur asynchrone qui met à jour _foo sur le changement. Je dois pouvoir accéder à _foo dans le composant fonctionnel comme je le fais avec this._foo dans la composante classique.

  • FunctionalBar ne devrait pas avoir _foo dans la portée globale dans le cas où il y a plus d'un FunctionalBar.
  • FunctionalBar ne peut pas avoir _foo dans l'étendue de la fonction car _foo est réinitialisé à chaque rendu de FunctionalBar. _foo ne doit pas non plus être dans l'état car le composant n'a pas besoin d'être rendu lorsque _foo changements.
  • ClassBar n'a pas ce problème car il conserve _foo initialisé sur this pendant toute la durée de vie du composant.

Comment puis-je conserver _foo initialisé tout au long de la vie de FunctionalBar sans le mettre dans le périmètre global?

Implémentation fonctionnelle

import React from 'react';
import { Animated, View } from 'react-native';

var _foo = 0;

function FunctionalBar(props) {

  const foo = new Animated.Value(0);

  _onChangeFoo({ value }) {
    _foo = value;
  }

  function showFoo() {
    let anim = Animated.timing(foo, { toValue: 1, duration: 1000, useNativeDriver: true });
    anim.start(() => console.log(_foo));
  }

  useEffect(() => {
    foo.addListener(_onChangeFoo);
    showFoo();
    return () => foo.removeListener(_onChangeFoo);   
  });

  return <View />;

}

Implémentation classique

import React from 'react';
import { Animated, View } from 'react-native';

class ClassBar extends React.Component {

  constructor(props) {
    super(props);
    this.state = { foo: new Animated.Value(0) };
    this._foo = 0;
    this._onChangeFoo = this._onChangeFoo.bind(this);
  }

  componentDidMount() {
    this.state.foo.addListener(this._onChangeFoo);
    this.showFoo();
  }

  componentWillUnmount() {
    this.state.foo.removeListener(this._onChangeFoo);
  }

  showFoo() {
    let anim = Animated.timing(this.state.foo, { toValue: 1, duration: 1000, useNativeDriver: true });
    anim.start(() => console.log(this._foo));
  }

  _onChangeFoo({ value }) {
    this._foo = value;
  }

  render() {
    return <View />;
  }

}
16
woodpav

Le crochet useRef n'est pas seulement pour les références DOM, mais peut stocker n'importe quelle valeur modifiable que vous aimez.

Exemple

function FunctionalBar(props) {
  const [foo] = useState(new Animated.Value(0));
  const _foo = useRef(0);

  function showFoo() {
    let anim = Animated.timing(foo, { toValue: 1, duration: 1000, useNativeDriver: true });
    anim.start(() => console.log(_foo.current));
  }

  useEffect(() => {
    function _onChangeFoo({ value }) {
      _foo.current = value;
    }

    foo.addListener(_onChangeFoo);
    showFoo();
    return () => foo.removeListener(_onChangeFoo);
  }, []);

  return <View />;
}
24
Tholle

C'est un exemple assez inhabituel, mais si je lis bien, vous voulez simplement stocker des objets _foo Uniques à chaque fois que le composant se monte et les détruire lors de son démontage, mais également empêcher des rediffusions supplémentaires lorsque cette valeur change.

J'ai déjà rencontré ce scénario et un objet simple (carte/hachage) devrait faire l'affaire:

let foos = {}
let fooCount = 0

function F(props) {
  useEffect(() => {
    let fooId = fooCount++
    foos[fooId] = new Animated.Value(0)
    foos[fooId].addListener(...)
    return () => foos[fooId].removeListener(...)
  }, []) // <-- do not rerun when called again (only when unmounted)

  ...render...
}

ou quelque chose à cet effet. si vous avez un exemple exécutable, vous pouvez le modifier pour qu'il corresponde mieux à votre exemple. de toute façon, la plupart des problèmes de portée sont résolus avec des primitives.

0
azium