Строчка
todo.completed = !todo.completed
выполняется при каждом клике дважды, поэтому сначала значение становится false, затем снова true, в то время как текстовая переменная прекрасно меняется. Мне немного стыдно, поскольку точно не знаю механизм работы реакта в данном случае, но выхода тут два:
1) 
if (todo.id === id) return { ...todo, completed: !todo.completed }
return todo
Что в действительности более правильно, так как каждый изменяемый элемент в реакте должен менять ссылку при изменении.
2) Вычислять новый completed сразу
onChange={() => props.handleChange(props.item.id, !props.item.completed)}
и уже в функции пользоваться полученным значением
handleChange(id, newCompleted) {
      this.setState(prevState => {
        
          const updatedTodos = prevState.todos.map(todo => {
              if (todo.id === id) {
                todo.completed = newCompleted 
              }
              return todo
          })
          return {
              todos: updatedTodos
          }
      })