下面是两个React组件,它们几乎做着相同的事情。一个是函数;另一个是班级。每个组件都有一个Animated.Value
和一个异步侦听器,该侦听器在更改时更新_foo
。我需要能够访问功能组件中的_foo
,就像我在经典组件中访问this._foo
一样。
FunctionalBar
,则FunctionalBar
不应在全局范围中具有_foo
FunctionalBar
不能在函数作用域中具有_foo
,因为每次FunctionalBar
渲染时都会重新初始化_foo
。_foo
也不应处于状态,因为_foo
更改时组件不需要渲染ClassBar
不存在此问题,因为它在组件的整个生命周期中保持_foo
在this
上初始化如何在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 />;
}
}
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 />;
}
您可以使用useRef钩子(这是文档中建议的方法(:
- 声明变量:
const a = useRef(5) // 5 is initial value
- 获取值:
a.current
- 设置值:
a.current = my_value
为了支持Tholle的回答,这里有官方文档
参考
但是,
useRef()
不仅仅用于ref属性。它是方便地保持任何可变值与您使用的值类似类中的实例字段。这是因为
useRef()
创建了一个纯JavaScript对象。这个useRef()
和创建{current: ...}
对象之间的唯一区别useRef
会在每个提供请记住,
useRef
不会在其内容发生更改时通知您。更改.current属性不会导致重新渲染。如果你愿意为了在React将ref附加或分离到DOM节点时运行一些代码,您可能需要使用回调ref。
我很幸运地使用了带有析构函数的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 />;
}
这是一个非常不寻常的例子,但如果我没有记错的话,您只需要在每次组件装载时存储唯一的_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...
}
或者类似的东西。如果你有一个可运行的例子,可以对它进行调整,使其更适合你的例子。无论哪种方式,大多数范围问题都是用基元来解决的。