有下一种情况-有隔离组件Timer: 从指定时间到哪一个滴答时间都为0。超过计时器的时间将启动回调函数。我对上面的实施情况没有问题,但是还有另一种情况。我需要通过父组件的某个事件来重新启动计时器-它可以是所有内容(用户单击等)。太...我该如何从父组件启动此事件?
在相同的情况下,我对子组件使用了ref,并通过ref统治了子组件和Child状态。但是我认为这是不好的做法。如此...也许有人还有另外一句话?
答案 0 :(得分:2)
您想要的是修改子组件的状态。这不是反应的原理。 React是一种方法-从上到下。
如果需要在某些组件之间共享某些数据,请将该数据移动到树中最近的公共父级。如果在反应树的许多不同节点中需要状态,最明显的是,当存在仅传递信息的中间组件时,请使用上下文或专用状态容器,例如redux。
这是使用上下文的计时器实现:
Timer
为所有子代后代提供时间信息和功能。
可以使用Time
组件获取当前时间,并可以使用ResetTime
重置功能。</ p>
此解决方案非常容易组合。通过这两个简单的组件,您可以在应用程序中的任何位置使用计时器功能。
import Timer, { Time, ResetTime } from "./Timer";
function App() {
return (
<Timer>
<div className="App">
<Time>{time => <p>{time}</p>}</Time>
<ResetTime>{reset => <button onClick={reset}>reset</button>}</ResetTime>
</div>
</Timer>
);
}
import React from "react";
const TimerContext = React.createContext();
export default class Timer extends React.Component {
reset = () => {
this.stop();
this.start();
};
stop = () => {
clearInterval(this.interval);
}
start = () => {
this.setState(({time}) => ({time: 15}))
this.interval = setInterval(() => {
this.setState(({ time }) => {
if (time > 0) {
return { time: time - 1 };
} else {
this.stop();
}
});
}, 1000);
};
state = {
time: 15,
reset: this.reset
};
componentDidMount() {
this.start();
}
componentWillUnmount() {
this.stop();
}
render() {
return (
<TimerContext.Provider value={this.state}>
{this.props.children}
</TimerContext.Provider>
);
}
}
export const TimerConsumer = TimerContext.Consumer;
export const Time = ({children}) => <TimerConsumer>
{({time}) => children(time)}
</TimerConsumer>
export const ResetTime = ({children}) => <TimerConsumer>
{({reset}) => children(reset)}
</TimerConsumer>
答案 1 :(得分:1)
在React中,您可以通过两种不同的方式让父级交流孩子。
Child.render()
ref
+调用某些方法。这个Child
的方法可以做任何事情,一旦调用了this.setState
组件,它将被重新渲染。 在您的情况下,使用道具重置计时器会很麻烦。它应该是布尔型(reset={true}
)还是字符串(current-time={'00:00'}
)还是数字?当我们重置计时器而不是正在运行的计时器并再次重置时,如何跟踪情况(将该道具设置为与reset={true}
完全相同的值没有意义)?我们需要在componentDidUpdate
中进行大量检查,以确保在重置计时器和不重置计时器时,情况有所不同。
另一方面,呼叫timerRef.reset()
看起来非常清晰直接。
不,使用ref
本身并不是一个坏习惯。 Uncontrolled components are