React.js and ES2015 - Pass a method from parent to child - javascript

I'm trying to remove the child element(Note) when a user click the remove button. The remove method is on parent(Board) and I try to pass it to child thru props, but it is not working.
I try to use simple remove, this.remove - not defined remove, or this, this.remove.bind(this) nothing seems to work;location: eachNote(text,i) method
import React from 'react';
import ReactDOM from 'react-dom';
class Note extends React.Component{
constructor(props){
super(props);
this.state = {editing: false};
}
edit() {
this.setState({editing: true});
}
save() {
let val = this.refs.newText.value;
this.setState({editing: false});
}
renderNormal(){
return (
<div>
<p>{this.props.children} </p>
<button type="button" onClick={this.edit.bind(this)}>Edit</button>
<button type="button" onClick={this.hRemove.bind(this)}>Remove</button>
</div>
);
}
renderForm(){
return (
<div>
<textarea ref="newText" defaultValue={this.props.children}></textarea>
<button type="button" onClick={this.save.bind(this)}>Saved</button>
</div>
);
}
render() {
if(this.state.editing ==true ) {return this.renderForm();}
else {return this.renderNormal();}
}
}
class Board extends React.Component{
constructor(props) {
super(props);
this.state = {comments: ['icecream','antimoniu','bentrans'] };
}
remove(i){
let arr = this.state.comments;
arr.splice(i,1);
this.setState({comments: arr});
}
eachNote(text,i) {
return (<Note key={i} index={i} hRemove={this.remove}>{text}</Note>);
}
render(){
return (
<div>
{this.state.comments.map(this.eachNote)}
</div>
);
}
}
ReactDOM.render(<Board />, document.getElementById('container'));
I tried Rafi Ud Daula Refat and Sven (thanks for answers) codes and the below one, but I still received the error: this is undefined;
in the Parent, I have:
eachNote(text,i) {
return (<Note key={i} index={i} hRemove={this.remove.bind(i)}>{text} </Note>);
}
in the Child, I have:
removed(i) {
this.props.hRemove(i);
}
renderNormal(){
return (
<div>
<p>{this.props.children} </p>
<button type="button" onClick= {this.edit.bind(this)}>Edit</button>
<button type="button" onClick= {this.removed.bind(this,i)}>Remove</button>
</div>
);
}
I tried also this.removed.bind(this) and this.removed.bind(i), hRemove={this.remove.bind(i)}, and their combinations not working

If you want to use one method of parent you should pass the function as a props to the child. and from child you can access it as
this.props.functionName
Here in your note Component
<button type="button" onClick={this.hRemove.bind(this)}>Remove</button>
But note component does not have any method named hRemove. It can be assed through
this.props.hRemove()
<button type="button" onClick={this.props.hRemove(idorsomething)}>Remove</button>
And as the function 'remove' in the parent component has one parameter. So from the Child component note you have pass variable a. then it will work. like
this.props.hRemove(id)

When you pass the function remove as a hRemoveprop to your Note, you can find it in this.props.hRemove.
You can also bind the Note directly to the passed remove function:
eachNote(text,i) {
return (<Note key={i} index={i} hRemove={this.remove.bind(i)}>{text}</Note>);
}
Then you simply use
<button type="button" onClick={this.props.hRemove}>Remove</button>
Keep in mind, with ES6 you don't have this in your custom render methods, this can be done in several ways: http://egorsmirnov.me/2015/08/16/react-and-es6-part3.html

Related

ReactJS remove dynamic element

i want to remove dynamic element in my program, but i think, i have problem with 'this'.When i click in 'X', nothing happens, console doesn't show any error. Maybe someone more experienced will help me.
('items' is array in state)
Main file:
removeItemCity(i){
let arr = this.state.items;
arr.splice(i, 1);
this.setState({items:arr})
}
renderItems(item,i){
return(<Tiles key = {'key_' + i} index = {i} delete = {() =>
{this.removeItemCity}}/>);
}
render() {
return(
<div className = "BodyAppContainer">
<div className = "grid" id="items">
{this.state.items.map(this.renderItems) }
</div>
</div>
);
}
And my component "Tiles"
import React from 'react';
class Tiles extends React.Component {
constructor(props) {
super(props);
}
remove(){
this.props.delete(this.props.index);
}
render() {
return (
<div className = "col-4_sm-6_xs-12 item">
<h2>City : {this.props.index}</h2>
<button className="removeButton" onClick={() => this.remove} >X</button>
</div>
);
}
}
export default Tiles;
Your onClick prop for the X button is not doing anything:
onClick={() => this.remove}
When you click, it calls that arrow function. But that arrow function only has this.remove, which is the definition to a method. The first step in helping you out is you should call that method using parentheses:
onClick={() => this.remove()}
The same thing applies to your renderItems(), where you are also missing parentheses to enact a function call in the delete prop passed to Tiles:
delete={() => {this.removeItemCity}}
Try this:
<button className="removeButton" onClick={this.remove} >X</button>

How to get a React component in a function by onClick

I need to get a component that I cliked and see its target property. I try to get it but the evt param is undefined
getcomponent(evt){
console.log(evt.target)
//...
}
//...
render() {
return (<button id="btn" onClick={() =>this.getcomponent()}></button>);
}
You didn't pass the event to function call. Pass the event like this:
onClick={(evt) => this.getcomponent(evt)}.
Add event as a parameter to the onClick:
render() {
return (<button id="btn" onClick={(event) =>this.getcomponent(event)}></button>);
}
Make code short and simple:
onClick = event => {
console.log(event.target)
}
render() {
return <button id="btn" onClick={this.onClick}></button>
}
You need to pass event in order to get it back. Here is the code.
class TestJS extends React.Component {
constructor(props) {
super(props);
this.getcomponent = this.getcomponent.bind(this);
}
getcomponent(event){
console.log(event.target);
}
render() {
return(
<div id="root">
<button id="btn" onClick={(event) =>this.getcomponent(event)}></button>;
</div>
)};
}
export default TestJS;

ReactJS: e.preventDefault() is not a function

I'm making a simple todo app, where i have put in the logic to edit and delete the todos as well. I'm trying to update the parent state from child component but when i'm trying to click on delete it is throwing me an error e.preventDefault() is not a function and it is removing all of the todos here are the components:
PARENT
export default class App extends React.Component {
constructor(props){
super(props);
this.state = {
listArr: [],
}
}
deleteTodos(i) {
var lists = this.state.listArr;
lists.splice(i, 1);
this.setState({listArr: lists})
}
render() {
.......
<ToDoList {...this.state} passDeleteTodos={this.deleteTodos} />
......
}
CHILD
export class ToDoList extends React.Component {
constructor(props) {
super(props);
this.state = {
editing: false,
};
handleDelete(e, i) {
e.preventDefault();
this.props.passDeleteTodos()
}
renderDisplay() {
return(
<div>
{
this.props.listArr.map((list,i) => {
return(
<div key={i} index={i} ref="text">
<li>{list}
<div style={{float: 'right'}}>
<button className="btn btn-danger btn-xs glyphicon glyphicon-trash"
onClick={() => this.handleDelete(i)}
/>
</div>
</div>
</div>
You need to pass the event object to handleDelete function when you make use of Arrow function as done in your implementation.
You can think of an arrow function like a function that calls another function to which you need to pass the arguments. Event object is a parameter to the arrow function and you indeed need to pass this on to the handleDelete function
onClick={(e) => this.handleDelete(e, i)}
However after this change you still need to bind the deleteTodos function in the parent, since the context of this inside this function won't be that of the React class component, you can do it like
deleteTodos = (i) => {
var lists = this.state.listArr;
lists.splice(i, 1);
this.setState({listArr: lists})
}
or
constructor(props){
super(props);
this.state = {
listArr: [],
}
this.deleteTodos = this.deleteTodos.bind(this);
}
I change e.preventDefault() => e.preventDefault and bind the function.
Example
export default class App extends React.Component {
constructor(props) {
super(props)
this.state = {
listArr: [],
}
this.deleteTodos = this.deleteTodos.bind(this)
}
handleDelete(e, i) {
e.preventDefault;
this.props.passDeleteTodos()
...
}
render() {
return(
<div>
{
this.props.listArr.map((list,i) => {
return(
<div key={i} index={i} ref="text">
<li>{list}
<div style={{float: 'right'}}>
<button className="btn btn-danger btn-xs glyphicon glyphicon-trash"
onClick={(e,i) => this.handleDelete(e,i)}
/>
</div>
</div>
)}
}
</div>
You are not sending e to the correspondent method.
You could also bind the event
onClick={this.handleDelete.bind(this, i)}
Same applies for deleteTodos in the App component.
Either way you can use the same approach or bind it in the constructor:
export default class App extends React.Component {
constructor(props) {
super(props)
this.state = {
listArr: [],
}
this.deleteTodos = this.deleteTodos.bind(this)
}
...
}
doesn't behave the same way as an so you can't expect the same preventDefault call.
But your problem is you in bind the order of params change. So you're binded param becomes first in the function. See my snippet below.
const App = () => {
const _click = (externalVar, e) => {
console.log("PARAMS", externalVar, e);
};
const externalVar = 1
return (
<button onClick={_click.bind(undefined, externalVar)}>click me</button>
);
};
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>
Like it says here
fun.bind(thisArg[, arg1[, arg2[, ...]]])
arg1, arg2, ... Arguments to prepend to arguments provided to the
bound function when invoking the target function.
arrow function in react doesn't need to bind to this.
But during call to the functions, for example to call this function handleDelete
handleDelete(e, i) {
e.preventDefault();
this.props.passDeleteTodos()
}
we will use synatx as:
handleDelete.bind(i)
handleDelete(e, i) {
e.preventDefault();
this.props.passDeleteTodos()
...
}
onClick={(e,i) => this.handleDelete(e,i)}
if the above code is not working properly try this.
handleDelete(i) {
this.props.passDeleteTodos()
...
}
onClick={(e,i) => {e.preventDefault(); this.handleDelete(i)}}

rendering multiple elements after onClick event in React

I'm having problems trying to render two react elements inside a react component after a onClick event. Wondering if that's even possible? I'm sure I'm messing up the ternary operator, but I cannot think on another way to do what I'm trying to do ?
TL;DR: "When I click a button I see elementA and elementB"
Here is a snippet of the code:
import React, { Component } from 'react';
class MyComponent extends Component {
constructor(props) {
super(props)
this.state = { showElement: true };
this.onHandleClick = this.onHandleClick.bind(this);
}
onHandleClick() {
console.log(`current state: ${this.state.showElement} and prevState: ${this.prevState}`);
this.setState(prevState => ({ showElement: !this.state.showElement }) );
};
elementA() {
<div>
<h1>
some data
</h1>
</div>
}
elementB() {
<div>
<h1>
some data
</h1>
</div>
}
render() {
return (
<section>
<button onClick={ this.onHandleClick } showElement={this.state.showElement === true}>
</button>
{ this.state.showElement
?
null
:
this.elementA() && this.elementB()
}
</section>
)
}
}
export default MyComponent;
You just inattentive.
elementA() {
return ( // You forget
<div>
<h1>
some data
</h1>
</div>
)
}
And the same in element B.
And if You want to see both components you should change Your ternary to
{ this.state.showElement
?
<div> {this.elementA()} {this.elementB()}</div>
:
null
}
Another "and", for toggling showElement in state just enough
this.setState({showElement: !this.state.showElement });
Try this instead, (I will add comments into the code trying to explain what's going on):
function SomeComponentName() { // use props if you want to pass some data to this component. Meaning that if you can keep it stateless do so.
return (
<div>
<h1>
some data
</h1>
</div>
);
}
class MyComponent extends Component {
constructor(props) {
super(props)
this.state = { showElement: false }; // you say that initially you don't want to show it, right? So let's set it to false :)
this.onHandleClick = this.onHandleClick.bind(this);
}
onHandleClick() {
this.setState(prevState => ({ showElement: !prevState.showElement }) );
// As I pointed out in the comment: when using the "reducer" version of `setState` you should use the parameter that's provided to you with the previous state, try never using the word `this` inside a "reducer" `setState` function
};
render() {
return (
<section>
<button onClick={ this.onHandleClick } showElement={this.state.showElement === false}>
</button>
{ this.state.showElement
? [<SomeComponentName key="firstOne" />, <SomeComponentName key="secondOne" />]
: null
}
</section>
)
}
}
export default MyComponent;

React.js: How to append a component on click?

I'm new to React and I'm puzzled on something kind of basic.
I need to append a component to the DOM after the DOM is rendered, on a click event.
My initial attempt is as follows, and it doesn't work. But it's the best thing I've thought to try. (Apologies in advance for mixing jQuery with React.)
ParentComponent = class ParentComponent extends React.Component {
constructor () {
this.addChild = this.addChild.bind(this);
}
addChild (event) {
event.preventDefault();
$("#children-pane").append(<ChildComponent/>);
}
render () {
return (
<div className="card calculator">
<p><a href="#" onClick={this.addChild}>Add Another Child Component</a></p>
<div id="children-pane">
<ChildComponent/>
</div>
</div>
);
}
};
Hopefully it's clear what I need to do, and I hope you can help me attain an appropriate solution.
Don't use jQuery to manipulate the DOM when you're using React. React components should render a representation of what they should look like given a certain state; what DOM that translates to is taken care of by React itself.
What you want to do is store the "state which determines what gets rendered" higher up the chain, and pass it down. If you are rendering n children, that state should be "owned" by whatever contains your component. eg:
class AppComponent extends React.Component {
state = {
numChildren: 0
}
render () {
const children = [];
for (var i = 0; i < this.state.numChildren; i += 1) {
children.push(<ChildComponent key={i} number={i} />);
};
return (
<ParentComponent addChild={this.onAddChild}>
{children}
</ParentComponent>
);
}
onAddChild = () => {
this.setState({
numChildren: this.state.numChildren + 1
});
}
}
const ParentComponent = props => (
<div className="card calculator">
<p><a href="#" onClick={props.addChild}>Add Another Child Component</a></p>
<div id="children-pane">
{props.children}
</div>
</div>
);
const ChildComponent = props => <div>{"I am child " + props.number}</div>;
As #Alex McMillan mentioned, use state to dictate what should be rendered in the dom.
In the example below I have an input field and I want to add a second one when the user clicks the button, the onClick event handler calls handleAddSecondInput( ) which changes inputLinkClicked to true. I am using a ternary operator to check for the truthy state, which renders the second input field
class HealthConditions extends React.Component {
constructor(props) {
super(props);
this.state = {
inputLinkClicked: false
}
}
handleAddSecondInput() {
this.setState({
inputLinkClicked: true
})
}
render() {
return(
<main id="wrapper" className="" data-reset-cookie-tab>
<div id="content" role="main">
<div className="inner-block">
<H1Heading title="Tell us about any disabilities, illnesses or ongoing conditions"/>
<InputField label="Name of condition"
InputType="text"
InputId="id-condition"
InputName="condition"
/>
{
this.state.inputLinkClicked?
<InputField label=""
InputType="text"
InputId="id-condition2"
InputName="condition2"
/>
:
<div></div>
}
<button
type="button"
className="make-button-link"
data-add-button=""
href="#"
onClick={this.handleAddSecondInput}
>
Add a condition
</button>
<FormButton buttonLabel="Next"
handleSubmit={this.handleSubmit}
linkto={
this.state.illnessOrDisability === 'true' ?
"/404"
:
"/add-your-details"
}
/>
<BackLink backLink="/add-your-details" />
</div>
</div>
</main>
);
}
}

Categories