Reputation: 2269
Working with ReactJS and having trouble understanding how callback functions
work with ReactJS.
I have a parent component titled TodoFormComponent
, which initializes my list of todo items. I've created a callback function on the TodoItemsComonent
, but it doesn't trigger the updateItem
method and display the selected
item.
Question: How can I pass data from the child to the parent? I want to pass the selected todo item to the parent so that I can update the master todo list.
Parent Component (TodoFormComponent)
The TodoFormComponent
has selectedTask
, which should be triggering the updateItem
method.
import * as React from "react";
import TodoItemsComponent from "../todo-items/todo-items.component";
import AddTodoItemComponent from "../add-todo-item/add-todo-item.component";
export default class TodoFormComponent extends React.Component {
constructor(){
super();
this.state = {
todoItems: [
{ id: '1', todo: 'First Todo Item' },
{ id: '2', todo: 'Second Todo Item' },
{ id: '3', todo: 'Third Todo Item' }
],
selected: {}
};
this.updateItem = this.updateItem.bind(this);
}
updateItem () {
console.log('Selected Value:: ', this.state.selected);
}
render() {
return (
<div className="row">
<div className="container">
<div className="well col-xs-6 col-xs-offset-3">
<h1>To do: </h1>
<div name="todo-items">
<TodoItemsComponent items={this.state.todoItems} selectedTask={() => {this.updateItem}}/>
</div>
<div name="add-todo-item">
<AddTodoItemComponent/>
</div>
</div>
</div>
</div>
)
}
}
Child Component (TodoItemsComponent)
The TodoItemsComponent
has an onClick
to update the selected value. This gets updated in the selectedTask
function.
import * as React from "react";
export default class TodoItemsComponent extends React.Component {
constructor(props) {
super(props);
}
selectedTask (item) {
this.setState({selected: item})
}
render() {
return (
<ul className="list-group">
{
this.props.items.map((item) => {
return (
<li className="list-group-item">
{item.todo}
<div className="pull-right">
<button
type="button"
className="btn btn-xs btn-success">
✓
</button> <button
type="button"
className="btn btn-xs btn-danger"
onClick={() => {this.selectedTask(item)}}
>X
</button>
</div>
</li>
)
})
}
</ul>
)
}
}
Upvotes: 8
Views: 48272
Reputation: 16472
Whenever you want to pass data from child to parent you pass a function as a prop to child and from child you call that function using this.props.passedFunction(yourDataToPassToParent)
Here from your parent component you are passing the selectedTask
function as prop, so you should call this.prop.selectedTask()
with the data to be passed to parent like:
<button
type="button"
className="btn btn-xs btn-danger"
onClick={() => {this.props.selectedTask(item)}}
>
X
</button>
Also the way you are passing the selectedTask
in your parent is wrong. You should pass it like this:
<TodoItemsComponent items={this.state.todoItems} selectedTask={this.updateItem}/>
Upvotes: 17
Reputation: 331
In your TodoItemsComponent,updateItem() is passed as prop. So you need to call the this.props.updateItem()
in your onClick method.
So your button should be:
<button
type="button"
className="btn btn-xs btn-danger"
onClick={() =>
{this.props.selectedTask(item)}}>X
</button>
and Update your parent components UpdateItem method to receive properties as item. Like this:
updateItem (e) {
console.log('Selected Value:: ', e);
}
And to pass method in children you need to
<TodoItemsComponent items=
{this.state.todoItems} selectedTask={this.updateItem}/>
If you do this: {()=>this.updateItem()}
then it will initialize the method. So you need just pass the function reference.
Upvotes: 1
Reputation: 11600
class TodoItemsComponent extends React.Component {
constructor(props) {
super(props);
}
render() {
return (
<ul className="list-group">
{this.props.items.map(item => {
return (
<li className="list-group-item">
{item.todo}
<div className="pull-right">
<button type="button" className="btn btn-xs btn-success">
✓
</button>{" "}
<button
type="button"
className="btn btn-xs btn-danger"
onClick={() => {
this.props.selectedTask(item);
}}
>
X
</button>
</div>
</li>
);
})}
</ul>
);
}
}
class TodoFormComponent extends React.Component {
constructor() {
super();
this.state = {
todoItems: [
{ id: "1", todo: "First Todo Item" },
{ id: "2", todo: "Second Todo Item" },
{ id: "3", todo: "Third Todo Item" }
],
selected: {}
};
this.updateItem = this.updateItem.bind(this);
}
updateItem(item) {
this.setState({ selected: item });
}
render() {
return (
<div className="row">
<div className="container">
<div className="well col-xs-6 col-xs-offset-3">
<h1>To do: </h1>
<h3>
Selected task: {JSON.stringify(this.state.selected)}
</h3>
<div name="todo-items">
<TodoItemsComponent
items={this.state.todoItems}
selectedTask={this.updateItem}
/>
</div>
<div name="add-todo-item" />
</div>
</div>
</div>
);
}
}
const App = () => <TodoFormComponent />;
ReactDOM.render(<App />, 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>
playground: https://codesandbox.io/s/LgrGKK9og
Upvotes: 1
Reputation: 299
Some pseudo-code:
class Parent extends React.Component {
render() {
return (<Child id={1} onClick={(id) => console.log(id)}/>);
}
}
class Child extends React.Component {
render() {
return (<div onClick={() => this.props.onClick(this.props.id)}></div>);
}
}
Console.log will output "1"
More: link
Upvotes: 0
Reputation: 5537
You have to send updateItem as a prop to the child Component.
const Parent = () =>
<div>
<TodoItemsComponent items={this.state.todoItems} selectedTask={updateItem}/>
</div>
Also update
updateItem (item) {
this.setState({ selected: item })
console.log( 'Selected Value:: ', item);
}
Upvotes: 0