Reputation: 61
I want to make a handleItemClick event to toggle the value in done to true, how can I do it? I am struggling to understand how to change it. Thanks a lot for your help I realll appreciate it
const TodoItem = (props) => <li onClick={props.onClick}>{props.item.text}</li>
class TodoList extends React.Component {
render() {
const { items, onClick } = this.props;
return (<ul onClick={onClick}>
{items.map((item, index) =>
<TodoItem item={item} key={index} onClick={this.handleItemClick.bind(this, item)}/>)}
</ul>);
}
handleItemClick(item, event) {
// Write your code here
}
}
const items = [ { text: 'Buy grocery', done: true },
{ text: 'Play guitar', done: false },
{ text: 'Romantic dinner', done: false }
];
const App = (props) => <TodoList
items={props.items}
onItemClick={(item, event) => { console.log(item, event) }}
/>;
document.body.innerHTML = "<div id='root'></div>";
const rootElement = document.getElementById("root");
ReactDOM.render(<App items={items}/>, rootElement);
Upvotes: 0
Views: 1023
Reputation: 39270
You can have App own the list of todo items and pass the items and setItems function you got from setState.
Using setState and passing it has a disadvantage because you can't easily use useCallback to optimize the handlers but the following will work:
const TodoItem = props => (
<li onClick={props.onClick}>
{props.item.text} {String(props.item.done)}
</li>
);
const TodoList = ({ items, setItems }) => {
const onClick = id => () =>
setItems(
items.map(item =>
item.id === id
? { ...item, done: !item.done }
: item
)
);
return (
<ul>
{items.map(item => (
<TodoItem
item={item}
//never use index for list especially if you remove
// or re arrange them
key={item.id}
onClick={onClick(item.id)}
/>
))}
</ul>
);
};
const App = props => {
//someone needs to own items so when you change it
// it will cause a re render
const [items, setItems] = useState([
{ id: 1, text: 'Buy grocery', done: true },
{ id: 2, text: 'Play guitar', done: false },
{ id: 3, text: 'Romantic dinner', done: false },
]);
return <TodoList items={items} setItems={setItems} />;
};
Upvotes: 0
Reputation: 5650
With your current setup, the items
are passed in as props. At some point, these items need to become component state so that they can be updated.
One option is to accept the items as props and use them as the initial state in the TodoList
. However, what you like want is for the App
component to have these set as the initial state in the useState
hook.
Then pass the onItemClick
prop so that a click on the TodoItem
component can call this method and update the proper item.
const TodoItem = props => (
<li onClick={props.onClick}>
{props.item.text} - {props.item.done.toString()}
</li>
);
class TodoList extends React.Component {
render() {
const { items, onItemClick } = this.props;
return (
<ul>
{items.map((item, index) => (
<TodoItem item={item} key={index} onClick={() => onItemClick(item)} />
))}
</ul>
);
}
}
const App = () => {
const [items, setItems] = React.useState([
{ text: "Buy grocery", done: true },
{ text: "Play guitar", done: false },
{ text: "Romantic dinner", done: false }
]);
return (
<TodoList
items={items}
onItemClick={itemToUpdate => {
const updatedItems = items.map(item => {
if (item === itemToUpdate) {
return { ...item, done: !item.done };
} else {
return item;
}
});
setItems(updatedItems);
}}
/>
);
};
const rootElement = document.getElementById("root");
ReactDOM.render(<App />, rootElement);
<script crossorigin src="https://unpkg.com/react@16/umd/react.development.js"></script>
<script crossorigin src="https://unpkg.com/react-dom@16/umd/react-dom.development.js"></script>
<div id="root"></div>
Upvotes: 1