web-dev-qa-db-ja.com

機能コンポーネントへの非状態変数の格納

以下は2つのReact機能するコンポーネントほぼ同じことです。1つは関数です;もう1つはクラスです。各コンポーネントには_Animated.Value_があり、変更時に__foo_を更新する非同期リスナー。クラシックコンポーネントで__foo_を使用する場合と同様に、機能コンポーネントで_this._foo_にアクセスできるようにする必要があります。

  • FunctionalBarが複数ある場合は、FunctionalBarのグローバルスコープに__foo_を含めないでください。
  • FunctionalBarがレンダリングされるたびに__foo_が再初期化されるため、FunctionalBarの関数スコープに__foo_を含めることはできません。 __foo_が変更されたときにコンポーネントがレンダリングする必要がないため、__foo_も状態であってはなりません。
  • ClassBarは、コンポーネントの存続期間を通じてthisで__foo_を初期化し続けるため、この問題はありません。

__foo_をグローバルスコープに入れずにFunctionalBarの存続期間を通じて初期化しておくにはどうすればよいですか?

機能の実装

_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 />;

}
_

従来の実装

_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

useRef フックはDOM参照用だけでなく、変更可能な任意の値を格納できます。

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

これはかなり珍しい例ですが、これを正しく読んでいる場合は、コンポーネントがマウントされるたびに一意の_fooオブジェクトを格納し、マウント解除時にそれらを破棄するだけでなく、この値が変更されたときに余分な再レンダリングを防止することもできます。

以前にこのシナリオに遭遇したことがあり、単純なオブジェクト(マップ/ハッシュ)がうまくいくはずです。

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...
}

またはその効果に何か。実行可能な例がある場合は、それを調整して例に合わせてください。いずれにせよ、スコープの問題を伴うほとんどのものはプリミティブで解決されます。

0
azium