Good evening.

At the moment I am performing an assignment where I have encountered difficulty. I have a to-do list with the option of time-tracking tasks. The problem is that the user can run only one program at a time, i.e. he has to start the timer of only one task and at this time the buttons on the other timers are inactive to start.

Here is my code, I suspect that the problem is in setState, but I can not figure it out to the end. Timers appear, work, but continue to work simultaneously with each press on the start.

import React, { Component, PropTypes } from 'react' import classnames from 'classnames' import TodoTextInput from './TodoTextInput' export default class TodoItem extends Component { constructor(props) { super(props); this.state = { secondsStart: this.props.minSeconds, timerRunning: false } } static propTypes = { todo: PropTypes.object.isRequired, deleteTodo: PropTypes.func.isRequired, completeTodo: PropTypes.func.isRequired, } static defaultProps = { minSeconds: 0 } handleSave = (id, text) => { if (text.length === 0) { this.props.deleteTodo(id) } } handleStartClick = () => { if (!this.state.timerRunning) { this.incrementer = setInterval(() => { this.setState({ secondsStart: (this.state.secondsStart + 1) }); }, 1000) this.setState({ timerRunning: true, currentTodoId: this.props.todo.id, runningTodoId: this.props.todo.id }); } } getSeconds = () => { return ('0' + this.state.secondsStart % 60).slice(-2) } getMinutes = () => { return Math.floor((this.state.secondsStart / 60)%60) } getHoures = () => { return Math.floor((this.state.secondsStart / 3600)%24) } handleStopClick = () => { clearInterval(this.incrementer) this.setState({ timerRunning: false, currentTodoId: null, runningTodoId: null }); } render() { const { todo, completeTodo, deleteTodo} = this.props const element = this.state.todo ? ( <TodoTextInput text={todo.text} onSave={(text) => this.handleSave(todo.id, text)} /> ) : ( <div className="view"> <input className="toggle" type="checkbox" checked={todo.completed} onChange={() => completeTodo(todo.isRequired)} /> <label> {todo.text} </label> <div className="buttons"> <h6>{this.getHoures()}:{this.getMinutes()}:{this.getSeconds()}</h6> {(this.state.secondsStart === 0) ? <button className="timer-start" onClick={this.handleStartClick} disabled={this.state.timerRunning }>Start</button> : <button className="timer-stop" onClick={this.handleStopClick} disabled={!this.state.timerRunning && this.state.runningTodoId !== this.state.currentTodoId}>Stop</button> } </div> <button className="destroy" onClick={() => deleteTodo(todo.id)} /> </div> ) return ( <li className={classnames({ completed: todo.completed, })}> {element} </li> ) } } 

Also note that TodoItem has a parent, TodoTextInput, through which my Items are generated and I thought that maybe the timer needed to be generated from there, but still the solution is closer to me.

Thank you in advance!

    1 answer 1

    This issue has been resolved. It was necessary to correctly link the parent element and child.

    Change in child component:

     import React, { Component, PropTypes } from 'react' import classnames from 'classnames' import moment from 'react-moment' let interval; export default class TodoItem extends Component { static propTypes = { todo: PropTypes.object.isRequired, deleteTodo: PropTypes.func.isRequired, completeTodo: PropTypes.func.isRequired } componentWillUnmount() { clearInterval(interval); } handleDeleteClick = () => { this.props.deleteTodo(this.props.todo.id); } handleStartClick = () => { this.props.startTimer(this.props.todo.id); interval = setInterval(() => { this.props.updateTimer(this.props.todo.id); }, 1000); } handleStopClick = () => { this.props.stopTimer(this.props.todo.id); clearInterval(interval); } handleCompleteClick = () => { this.props.completeTodo(this.props.todo.id); this.props.stopTimer(this.props.todo.id); clearInterval(interval); } render() { const { todo, timerActive, timerTodo } = this.props return ( <li className={classnames({ completed: todo.completed })}> <div className="view" style={{ display: 'flex', alignItems: 'center' }} onClick={this.handleSelectToDo}> <input className="toggle" type="checkbox" checked={todo.completed} onChange={this.handleCompleteClick} /> <label style={{ width: '40%' }}> {todo.text} </label> <span style={{ display: 'block', fontSize: 16 }}>Time is {todo.total}</span> {(!timerActive || timerTodo === todo.id) && ( <button style={{ background: 'transparent', border: 0, outline: 0, fontSize: 12, cursor: 'pointer', marginLeft: 30 }} disabled={timerActive && timerTodo !== todo.id} onClick={timerActive ? this.handleStopClick : this.handleStartClick} >{timerActive ? 'Stop' : 'Start'}</button> )} <button className="destroy" onClick={this.handleDeleteClick} /> </div> </li> ) } } 

    Change in parent component:

      return ( <section className="main"> <ul className="todo-list"> {todos.map(todo => <TodoItem key={todo.id} todo={todo} {...actions} timerActive={timerActive} timerTodo={timerTodo} todototal={todo.total}/> )} </ul> {this.renderFooter(completedCount)} </section> ) 

    Reducers:

     case START_TIMER: { const todo = { ...state.todos.find(item => item.id === action.id) }; todo.startTime = new Date(); const todos = [...state.todos].map(item => item.id === action.id ? todo : item); return { ...state, timerActive: true, timerTodo: action.id, todos }; } case STOP_TIMER: { return { ...state, timerActive: false, timerTodo: null } } case UPDATE_TODO_TOTAL: { const todo = { ...state.todos.find(item => item.id === action.id) }; todo.total += 1; const todos = [...state.todos].map(item => item.id === action.id ? todo : item); const total = state.total || 0; return { ...state, todos, total: total + 1 }; } 
    • To supplement your question, we ask you to use the edit option. The “Post Reply” button should only be used for comprehensive answers to questions. - From the queue of checks - Vadizar
    • @Vadizar, what addition? This is not an addition to the question. She wrote that she solved her task and wrote exactly how - Yuri
    • Complete your answer with a more detailed solution description or code. Now your answer looks like a comment. - Yuri
    • @Yuri, the answer is supplemented by an example of changes to the code. - Valeria Shpiner
    • @ValeriaShpiner, great! :) - Yuri