#reactjs
#reactjs
Вопрос:
Я совершенно новичок в React. То, что я пытаюсь реализовать, — это таймер. При нажатии на часы, минуту или секунду таймер остановится и для выбранного таймера превратится в поле ввода, при нажатии кнопки ввода больше не должно отображаться никаких полей ввода и часы запускаются обратно. как это выглядит
Я пытаюсь прекратить передачу новых реквизитов дочернему компоненту, когда щелкаю по контейнеру flexbox. Я написал функцию handleClick и setInterval() или clearInterval() на основе переменной обновления в состоянии.
Что я хочу, так это когда я нажимаю на любой из часов / минут / секунд, поле выбора изменится на поле ввода, и таймер остановится. Как только я нажму enter, он вернется к таймеру.
class Timer extends React.Component{
constructor(props){
super (props);
this.timerRef = React.createRef();
this.state = {
hh: new Date().getHours().toString().padStart(2,"0"),
mm: new Date().getMinutes().toString().padStart(2,"0"),
ss: new Date().getSeconds().toString().padStart(2,"0"),
suffix: new Date().getHours() > 12 ? "PM" : "AM",
update:true,
};
}
tick() {
this.setState({
hh: new Date().getHours().toString().padStart(2,"0"),
mm: new Date().getMinutes().toString().padStart(2,"0"),
ss: new Date().getSeconds().toString().padStart(2,"0"),
suffix: new Date().getHours() > 12 ? "PM" : "AM",
})
}
componentDidMount(){
this.intervalId = setInterval(
() => this.tick(), 1000
);
}
componentWillUnmount(){
clearInterval(this.intervalId);
}
handleClick(){
this.setState(state => ({update: !state.update}));
console.log("1",this.state.update);
if (this.state.update){
this.intervalId = setInterval(
() => this.tick(), 1000
);
console.log("2 set interval",this.intervalId);
}
else{
clearInterval(this.intervalId);
console.log("2 clear interval");
}
}
render() {
const { hh, mm, ss, suffix } = this.state;
return (
<div className="box" > London Clock
<div className="flexbox-container" onClick={() => this.handleClick()}>
<Content time={hh}></Content>
<div><p>:</p></div>
<Content time={mm}></Content>
<div><p>:</p></div>
<Content time={ss}></Content>
<div className="suffix"><p>{suffix}</p></div>
</div>
</div>
);
}
}
class Content extends React.Component {
state = {
editMode: false,
time: ""
};
componentDidMount(){
this.setState({
time: this.props.time,
})
}
handleKeyDown(event){
console.log(event,event.key === 'Enter');
if (event.key === 'Enter'){
this.setState({
editMode: false,
})
}
}
render() {
const {editMode} = this.state;
return (
<div>
{editMode? (
<p>
<input
defaultValue={this.props.time}
onKeyPress={e => this.handleKeyDown(e)}
/>
</p>
) : (
<p onClick={() => this.setState({ editMode: true })}>{this.props.time}</p>
)}
</div>
);
}
}
ReactDOM.render(
<Timer/>,
document.body
);
.flexbox-container {
display: flex;
flex-direction: row;
}
.suffix{
padding-left: 20px;
}
.box{
border-style: solid;
padding: 10px;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/16.6.3/umd/react.production.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/16.6.3/umd/react-dom.production.min.js"></script>
Ответ №1:
Когда ваш компонент смонтирован, он запустит интервал и назначит его intervalId
.
Ваш clickhandler изменяет состояние, а затем немедленно пытается просмотреть состояние, не дожидаясь его обновления. Состояние, вероятно, не обновляется на данный момент, поэтому оно переназначает интервал, что приводит к обновлению зомби.
Либо передайте обратный вызов в setState(updater, [callback])
, либо переместите свою интервальную логику в componentDidUpdate
, что позволит вам отменить дублирование интервальной логики
Комментарии:
1. Спасибо, это хорошо объяснено и действительно имеет смысл. Оба метода работают в моей ситуации.
Ответ №2:
Попробуйте, как показано ниже, вместо настройки состояния и привязки вашей tick
функции.
componentDidMount(){
this.intervalId = setInterval(
() => this.tick.bind(this), 1000
);
}
componentWillUnmount(){
clearInterval(this.intervalId);
}
Комментарии:
1. Это не работает. Я чувствую, что проблема в функции handleClick, даже если я вижу, что программа выполняет clearInterval внутри handleClick, она все еще продолжает обновлять дочерние компоненты.
Ответ №3:
Я думаю, вы можете использовать Ref React вместо состояния.
constructor(props) {
this.timerRef = React.createRef();
}
componentDidMount() {
this.timerRef.current = setInterval(this.tick, 1000);
}
componentWillUnmount() {
clearInterval(this.timerRef.current);
}
Комментарии:
1. Спасибо за ответ, но моя настоящая проблема в том, что я пытаюсь clearInterval в handleClick() на основе его состояния, и это не сработает. componentDidMount() и componentWillUnmount() выглядят нормально.
2. Да, вы также можете использовать этот подход в handleClick. У меня была такая же проблема в моем прошлом проекте, и я решил проблему таким образом. Я думаю, вам следует сохранить идентификатор интервала в ref вместо состояния
3. Я думаю, что использовать ссылку работает, и @dube для меня проще. Спасибо за ваш ответ!