Reputation: 42
I'm currently learning React and am following this guy Youtube Videos.
However, they are outdated and it's difficult to get along with the old codes. I already tried some Stackoverflow and Google solutions, but they didn't help.
Right now am I struggling on how I can access my variable todo
which I declared inside render()
and update it with a my function handleDelete
outside of render()
?
My goal is to delete the item i clicked on.
I already tried to set my variable inside constructor() but then it wasn't possible to give it the value of this.props.todos.
My code:
import React from 'react';
import ReactDom from 'react-dom';
export default class TodoItem extends React.Component {
handleDelete(item){
let updatedTodos = this.props.todos;
updatedTodos = updatedTodos.filter((val,index) => {
return item !== val;
})
todos = updatedTodos;
};
render() {
//Add all this.props items
let todos = this.props.todos;
todos = todos.map((item, index) => {
return (
<li>
<div className="todo-item">
<span className="item-name">{item}</span>
<span className="item-remove" onClick={this.handleDelete.bind(this, item)}> x </span>
</div>
</li>);
});
return (<React.Fragment>{todos}</React.Fragment>)
};
}
(This code is later exported to index.js where it is transpiled with Babel)
Thanks for your time taken!
Update:
Here is index.js
:
import React from 'react';
import ReactDom from 'react-dom';
import TodoItem from './todoItem';
class TodoComponent extends React.Component {
constructor(props) {
super(props);
this.state = {
todos: ["clean up", "walk doggo", "take nap"]
};
}
render() {
return (<div>
<h1>The todo list:</h1>
<ul>
<TodoItem todos={this.state.todos}/>
</ul>
</div>);
}
}
ReactDom.render(<TodoComponent/>, document.querySelector(".todo-wrapper"));
Upvotes: 0
Views: 5651
Reputation: 1119
TodoComponent
import React from 'react';
import ReactDom from 'react-dom';
import TodoItem from './todoItem';
class TodoComponent extends React.Component {
constructor(props) {
super(props);
this.state = {
todos: ["clean up", "walk doggo", "take nap"]
};
}
handleDelete(item){
let todos = this.state.todos;
todos= todos.filter((todo) => todo !== item);
this.setState((prevState) => ({
todos: todos
}));
};
render() {
return (<div>
<h1>The todo list:</h1>
<ul>
<TodoItem todos={this.state.todos} handleDelete={this.handleDelete}/>
</ul>
</div>);
}
}
ReactDom.render(<TodoComponent/>, document.querySelector(".todo-wrapper"));
Todo Item
import React from 'react';
import ReactDom from 'react-dom';
export default class TodoItem extends React.Component {
render() {
return (
this.props.todos.map((item) => {
return (
<li>
<div className="todo-item">
<span className="item-name">{item}</span>
<span className="item-remove" onClick={() => this.props.handleDelete(item)}> x </span>
</div>
</li>
)
}))
}
}
Your code is having the problem in TodoItem
that you are trying to delete items in TodoItem
where you do not have access to state. And moreover If you do some actions in component and you want to get the change reflected the your components must re render. And this is possible when your state is changed. And component related to corresponding changes will re render itself
Upvotes: 3
Reputation: 17598
I'm suggesting a different approach for you. There are some issues you need to think better. First of all, I suggest keeping your todo
s as objects and let have them id
and text
properties.
Second, you have a separate component but you are passing to it whole todos
. Instead of that, map your todos
and then pass each todo
to your component. In this way your management over everything will be easier.
For your situation, you need a delete handler to pass your TodoItem
, then by using this handler you will do the action.
Lastly, your TodoItem
does not need to be a class component, so I've changed it.
Here is a working example. I've changed your code according to my suggestions.
class Todos extends React.Component {
state = {
todos: [
{ id: 1, text: "foo" },
{ id: 2, text: "bar" },
{ id: 3, text: "baz" },
]
}
deleteTodo = ( todo ) => {
const newTodos = this.state.todos.filter( el => el.id !== todo.id );
this.setState({ todos: newTodos });
}
render() {
const { todos } = this.state;
return (
<div>
<ul>
{
todos.map( todo => <TodoItem key={todo.id} todo={todo} deleteTodo={this.deleteTodo} /> )
}
</ul>
</div>
)
}
}
const TodoItem = props => {
const handleDelete = () => props.deleteTodo(props.todo);
return (
<li onClick={handleDelete}>{props.todo.text} x</li>
)
};
ReactDOM.render(<Todos />, document.getElementById("root"));
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/15.1.0/react.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/15.1.0/react-dom.min.js"></script>
<div id="root"></div>
Also, last night I wrote a small example for another question. But it got bigger and bigger and I did not post it. But here is a small, simple Todo example. Of course there must be some improvements but you can examine it as an example.
https://codesandbox.io/s/01v3kmp9vv
Edit
If you don't want to use class-fields
and install another Babel plugin, just change the related part with this:
class Todos extends React.Component {
consturctor( props ) {
super( props );
this.state = {
todos: [
{ id: 1, text: "foo" },
{ id: 2, text: "bar" },
{ id: 3, text: "baz" },
],
}
}
....rest of the code
Upvotes: 1
Reputation: 2661
I have not tested it so there might be some typos but you have to do it like this
import React from 'react';
import ReactDom from 'react-dom';
export default class TodoItem extends React.Component {
handleDelete(item){
this.props.updateTodos(item)
};
render() {
//Add all this.props items
let todos = this.props.todos;
todos = todos.map((item, index) => {
return (
<li>
<div className="todo-item">
<span className="item-name">{item}</span>
<span className="item-remove" onClick={this.handleDelete.bind(this, item)}> x </span>
</div>
</li>);
});
return (<React.Fragment>{todos}</React.Fragment>)
};
}
import React from 'react';
import ReactDom from 'react-dom';
import TodoItem from './todoItem';
class TodoComponent extends React.Component {
constructor(props) {
super(props);
this.state = {
todos: ["clean up", "walk doggo", "take nap"]
};
this.updateTodos =this.updateTodos.bind(this);
}
updateTodos(item){
this.setState({todos :this.state.todos.filter((val,index) => {
return item !== val;
})})
}
render() {
return (<div>
<h1>The todo list:</h1>
<ul>
<TodoItem todos={this.state.todos} updateTodos ={this.updateTodos}/>
</ul>
</div>);
}
}
ReactDom.render(<TodoComponent/>, document.querySelector(".todo-wrapper"));
Upvotes: 1