Reputation: 105
I'm struggling to fix some issues regarding React JS. Currently worked on a crash course, and I've been trying to improve the todoList. I'm very new and hopefully this might give me a new perspective after already 8 hours of troubleshooting.
My code - Input:
export class TodoItem extends Component {
getStyle = () => {
return {
background: '#233D4D',
padding: '15px',
borderBottom: '1px darkgray Ridge',
textDecoration: this.props.todo.completed ? 'line-through' :
'none',
color: this.props.todo.completed ? 'lightgreen' : 'white',
fontWeight: this.props.todo.completed ? 'bold' : 'none',
}
}
render() {
const { title } = this.props.todo;
return (
<div style={this.getStyle()}>
<p>
<input type="checkbox" onChange= .
{this.props.markComplete.bind(this)} checked= .
{this.props.todo.completed} /> {' '}
{title}
<button style={btnStyle} onClick= .
{this.props.delTodo.bind(this)}><FontAwesomeIcon size="2x" icon= .
{faTrash} /></button>
</p>
</div>
)
}
}
// PropTypes
TodoItem.propTypes = {
Todos: PropTypes.array.isRequired,
markComplete: PropTypes.func.isRequired,
delTodo: PropTypes.func.isRequired
}
My code - Failed propType:
render() {
const { title } = this.props.todo;
return (
<div style={this.getStyle()}>
<p>
<input type="checkbox"
onChange={this.props.markComplete.bind(this)}
checked={this.props.todo.completed} /> {' '}
{title}
<button style={btnStyle}
onClick={this.props.delTodo.bind(this)}>
<FontAwesomeIcon size="2x" icon={faTrash} />
</button>
</p>
</div>
)
}
// PropTypes
TodoItem.propTypes = {
Todos: PropTypes.array.isRequired,
markComplete: PropTypes.func.isRequired,
delTodo: PropTypes.func.isRequired
}
Heres my issues:
#1 - Prop Types
index.js:1446 Warning: Failed prop type: The prop `Todos` is marked as required in `TodoItem`, but its value is `undefined`.
in TodoItem (at Todos.js:12)
#2 - Component changing an uncontrolled input
Warning: A component is changing an uncontrolled input of type text to
be controlled. Input elements should not switch from uncontrolled to
controlled (or vice versa). Decide between using a controlled or
uncontrolled input element for the lifetime of the component.`
======== EDIT =========
Heres where the components called, properties passed and the manipulation:
render() {
return (
<Router>
<div className="App">
<div className="container">
<Header />
<Route exact path="/" render={props => (
<React.Fragment>
<AddTodo addTodo={this.addTodo} />
<Todos todos={this.state.todo} markComplete= .
{this.markComplete}
delTodo={this.delTodo} />
</React.Fragment>
)} />
<Route path="/about" component={About} />
</div>
</div>
</Router>
);
class Todos extends Component {
render() {
// Mangler håndtering af ingen elementer
let output = undefined;
if(this.props.todos && this.props.todos.length > 0){
// lav object
let output = this.props.todos.map((todo) => (
<TodoItem key={todo.id} todo={todo} markComplete=
{this.props.markComplete} delTodo={this.props.delTodo} />
))
return output;
}
return (
<div>
{output}
</div>
/*this.props.todos.map((todo) => (
<TodoItem key={todo.id} todo={todo} markComplete=
{this.props.markComplete} delTodo={this.props.delTodo} />
))*/
);
}
}
Upvotes: 0
Views: 315
Reputation: 4995
I cleaned the mess in your code a bit and it is now working for me:
const TodoItem = ({title, completed, delTodo, markComplete}) => (
<div>
<p>
<input type="checkbox" onChange={markComplete} checked={completed} />
{title}
<button onClick={delTodo}>Delete</button>
</p>
</div>
);
TodoItem.propTypes = {
title: PropTypes.string.isRequired,
completed: PropTypes.bool.isRequired,
markComplete: PropTypes.func.isRequired,
delTodo: PropTypes.func.isRequired
};
class Todos extends Component {
constructor(props) {
super(props);
this.state = {
todos: [
{id: 1, title: "First", completed: false},
{id: 2, title: "Second", completed: false},
{id: 3, title: "Third", completed: true}
]
};
}
markComplete = id => {
const index = this.state.todos.findIndex(t => t.id === id);
if (index > -1) {
const modifiedTodos = JSON.parse(JSON.stringify(this.state.todos));
modifiedTodos[index].completed = true;
this.setState({todos: modifiedTodos});
}
};
delTodo = id => {
const index = this.state.todos.findIndex(t => t.id === id);
if (index > -1) {
const modifiedTodos = JSON.parse(JSON.stringify(this.state.todos));
modifiedTodos.splice(index, 1);
this.setState({todos: modifiedTodos});
}
};
render() {
return (
<div>
{this.state.todos
? this.state.todos.map(todo => (
<TodoItem
key={todo.id}
title={todo.title}
completed={todo.completed}
markComplete={() => this.markComplete(todo.id)}
delTodo={() => this.delTodo(todo.id)}
/>
))
: null}
</div>
);
}
}
Some comments about your code:
Todos
as property for TodoItem
, but you didn't set that property when using TodoItem
and because you set it as required with .isRequired
, the first error had been thrown.undefined
to some function. You didn't paste your code with that function, so I can not tell, what was going wrong exactly, but I think the problem is the binding of the functions markComplete
and delTodo
you provided TodoItem
via prop. Usually this binds the this
object to the current execution context (class TodoItem
in this case) and because TodoItem
has no member functions markComplete
and delTodo
itself, the binding returns undefined
for them.Todos
and TodoItem
you don't have any state, so better use stateless function components (much more compact).Upvotes: 1