【发布时间】:2020-01-18 11:34:29
【问题描述】:
我使用 React Context 来存储数据并提供修改这些数据的功能。
现在,我正在尝试使用 React Hooks 将类组件转换为功能组件。
虽然类中的一切都按预期工作,但我没有让它在功能组件中工作。
由于我的应用程序代码有点复杂,我创建了这个小示例 (JSFiddle link),它允许重现问题:
首先是上下文,对于类和功能组件来说都是一样的:
const MyContext = React.createContext();
class MyContextProvider extends React.Component {
constructor (props) {
super(props);
this.increase = this.increase.bind(this);
this.reset = this.reset.bind(this);
this.state = {
current: 0,
increase: this.increase,
reset: this.reset
}
}
render () {
return (
<MyContext.Provider value={this.state}>
{this.props.children}
</MyContext.Provider>
);
}
increase (step) {
this.setState((prevState) => ({
current: prevState.current + step
}));
}
reset () {
this.setState({
current: 0
});
}
}
现在,这是 Class 组件,它工作得很好:
class MyComponent extends React.Component {
constructor (props) {
super(props);
this.increaseByOne = this.increaseByOne.bind(this);
}
componentDidMount () {
setInterval(this.increaseByOne, 1000);
}
render () {
const count = this.context;
return (
<div>{count.current}</div>
);
}
increaseByOne () {
const count = this.context;
if (count.current === 5) {
count.reset();
}
else {
count.increase(1);
}
}
}
MyComponent.contextType = MyContext;
预期的结果是,它以一秒的间隔计数到 5 - 然后从 0 重新开始。
这是转换后的功能组件:
const MyComponent = (props) => {
const count = React.useContext(MyContext);
const increaseByOne = React.useCallback(() => {
console.log(count.current);
if (count.current === 5) {
count.reset();
}
else {
count.increase(1);
}
}, []);
React.useEffect(() => {
setInterval(increaseByOne, 1000);
}, [increaseByOne]);
return (
<div>{count.current}</div>
);
}
不是将计数器重置为 5,而是继续计数。
问题在于,if (count.current === 5) { 行中的 count.current 始终是 0,因为它不使用最新值。
我让它工作的唯一方法是按以下方式调整代码:
const MyComponent = (props) => {
const count = React.useContext(MyContext);
const increaseByOne = React.useCallback(() => {
console.log(count.current);
if (count.current === 5) {
count.reset();
}
else {
count.increase(1);
}
}, [count]);
React.useEffect(() => {
console.log('useEffect');
const interval = setInterval(increaseByOne, 1000);
return () => {
clearInterval(interval);
};
}, [increaseByOne]);
return (
<div>{count.current}</div>
);
}
现在,每次上下文更改都会重新创建 increaseByOne 回调,这也意味着每秒调用一次效果。
结果是,它会在每次更改上下文时清除间隔并设置一个新间隔(您可以在浏览器控制台中看到)。
这在这个小例子中可能有效,但它改变了原来的逻辑,并且开销更大。
我的应用程序不依赖于时间间隔,但它正在侦听一个事件。删除事件侦听器并稍后再次添加它意味着,如果它们在侦听器的移除和绑定之间被触发,我可能会丢失一些事件,这是由 React 异步完成的。
有人有想法,React 应该如何在不改变一般逻辑的情况下解决这个问题?
我在这里创建了一个小提琴,来玩弄上面的代码:
https://jsfiddle.net/Jens_Duttke/78y15o9p/
【问题讨论】:
标签: javascript reactjs react-hooks react-context