【问题标题】:Storing non-state variables in functional components在功能组件中存储非状态变量
【发布时间】:2019-04-08 08:25:40
【问题描述】:

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

  • FunctionalBar 不应在全局范围内有 _foo,以防有多个 FunctionalBar
  • FunctionalBar 不能在函数范围内包含 _foo,因为每次 FunctionalBar 呈现时都会重新初始化 _foo_foo 也不应该处于状态,因为当 _foo 更改时组件不需要渲染。
  • ClassBar 没有这个问题,因为它在组件的整个生命周期中保持_foothis 上初始化。

如何在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 />;
  }

}

【问题讨论】:

  • 你试过const foo = useState(new Animated.Value(0));吗?
  • 这并没有解决问题,因为我仍然需要附加监听器。这是一个范围问题。
  • 我仍然不确定您要达到的目标。当然,在实例上记录一些东西显然在函数组件中不起作用,但是你想用_foo做什么呢?
  • useEffect 中发出命令式调用。
  • 我可以拥有一个包含所有 _foo 的全局对象,并在卸载时清理它。感觉需要另一个钩子(或传递给useEffect的范围)。

标签: javascript reactjs react-native react-hooks


【解决方案1】:

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

【讨论】:

  • 为什么会这样?在非 React 函数中,您可以直接在函数内部设置 _foo(前提是您将其从 const 更改为 let),只要它仍在作用域内。这里发生了什么让这一切变得不同?
  • useRef 返回一个对象,其引用在重新渲染时不会改变,然后 foo 的实际值保存在该对象的当前属性中。 @Scribblemacher
【解决方案2】:

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

Reference

不过,useRef() 的用处不止是 ref 属性。它的 方便保持任何可变值与您的使用方式相似 类中的实例字段。

这是因为useRef() 创建了一个普通的JavaScript 对象。这 useRef() 和创建 {current: ...} 对象之间的唯一区别 你自己是 useRef 会给你相同的 ref 对象 渲染。

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

【讨论】:

    【解决方案3】:

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

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

    【讨论】:

      【解决方案4】:

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

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

      【讨论】:

      • 在函数 F 外部和内部声明 foos 有什么区别?
      • @vikrant 组件函数在每次渲染时都会被调用,因此如果您将普通变量声明放入其中,那么它们在每次渲染时都会被覆盖。
      • 如果您在任何时候页面上只有一个组件实例,这将很好。但是,如果您有多个组件,则在组件函数之外声明的变量将用于所有组件。我发现最好使用 useRef 方法。
      【解决方案5】:

      我很幸运地使用了带有解构的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 />;
      }
      

      【讨论】:

        猜你喜欢
        • 2021-12-24
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2021-05-05
        • 1970-01-01
        • 2011-03-22
        • 1970-01-01
        相关资源
        最近更新 更多