在功能组件中存储非状态变量

Storing non-state variables in functional components

下面是两个 几乎 做同样事情的 React 组件。一个是函数;另一个是class。每个组件都有一个 Animated.Value 和一个异步侦听器,在更改时更新 _foo。我需要能够访问功能组件中的 _foo,就像我在 classical 组件中访问 this._foo 一样。

如何在 FunctionalBar 的整个生命周期中保持 _foo 初始化而不将其放入全局范围?

功能实现

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

}

这是一个非常不寻常的例子,但如果我没看错的话,你只是想在每次组件安装时存储唯一的 _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...
}

或类似的东西。如果您有一个 运行nable 示例,可以调整它以使其更适合您的示例。无论哪种方式,大多数有范围问题的事情都是用原语解决的。

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

为了支持 Tholle 的回答这里是官方文档

Reference

However, useRef() is useful for more than the ref attribute. It’s handy for keeping any mutable value around similar to how you’d use instance fields in classes.

This works because useRef() creates a plain JavaScript object. The only difference between useRef() and creating a {current: ...} object yourself is that useRef will give you the same ref object on every render.

Keep in mind that useRef doesn’t notify you when its content changes. Mutating the .current property doesn’t cause a re-render. If you want to run some code when React attaches or detaches a ref to a DOM node, you may want to use a callback ref instead.

您可以使用 useRef hook (it's the recommended way stated in docs):

  • 声明变量:const a = useRef(5) // 5 is initial value
  • 获取值:a.current
  • 设置值:a.current = my_value

我有幸使用 useRef 钩子解构(+ 一个可选的变量别名“my”),然后你将所有值保存在 my 对象中,这样你就不会不必使用多个引用或一直使用 myref.current

function MyComponent(props) {
  const componentRef = useRef({});
  const { current: my } = componentRef;

  my.count = 42;
  console.log(my.count); // 42

  my.greet = "hello";
  console.log(my.greet); // hello

  return <div />;
}