68

下面是两个做几乎相同事情的 React 组件。一是函数;另一个是一个类。每个组件都有一个Animated.Value带有异步侦听器的异步侦听器,该侦听器会_foo根据更改进行更新。我需要能够_foothis._foo在经典组件中一样访问功能组件。

  • FunctionalBar不应该_foo在全局范围内有多个FunctionalBar.
  • FunctionalBar不能_foo在函数范围内,因为_foo每次FunctionalBar渲染时都会重新初始化。也不应该处于状态,因为组件在更改_foo时不需要渲染。_foo
  • ClassBar没有这个问题,因为它在组件的整个生命周期中一直处于_foo初始化状态。this

如何在不将其置于全局范围内的情况下_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 />;
  }

}
4

5 回答 5

101

useRef钩子不仅适用于 DOM refs,还可以存储您喜欢的任何可变值。

例子

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 />;
}
于 2018-11-05T00:03:01.773 回答
13

您可以使用useRef钩子(这是docs中推荐的方式):

  • 声明变量:const a = useRef(5) // 5 is initial value
  • 获取价值:a.current
  • 设置值:a.current = my_value
于 2020-11-05T11:17:30.030 回答
12

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

参考

然而,useRef()有用的不仅仅是 ref 属性。它可以方便地保留任何可变值,类似于您在类中使用实例字段的方式。

这是因为useRef()创建了一个普通的 JavaScript 对象。useRef()与自己创建{current: ...}对象之间的唯一区别是,它useRef会在每次渲染时为您提供相同的 ref 对象。

请记住,useRef它不会在其内容发生更改时通知您。改变 .current 属性不会导致重新渲染。如果你想在 React 将 ref 附加或分离到 DOM 节点时运行一些代码,你可能需要使用回调 ref 来代替。

于 2020-08-24T15:16:00.487 回答
2

这是一个非常不寻常的示例,但如果我没看错的话,您只想在_foo每次组件挂载时存储唯一的对象,并在卸载时销毁它们,同时还要防止在此值更改时进行额外的重新渲染。

我之前遇到过这种情况,简单的对象(map / hash)应该可以解决问题:

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

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

于 2018-11-04T23:59:57.500 回答
2

我很幸运地使用了useRef带有解构的钩子(+ 可选变量别名“my”),然后您将所有值保留在my对象中,这样您就不必使用多个 ref 或一直使用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 />;
}
于 2021-07-27T06:41:15.403 回答