How to pass methods with parameters to children in React? (Cannot read property 'props' of undefined) - reactjs

I'm currently making a to-do list, but unfortunately ran into some errors where I am unable to pass methods to a child component.
const React = require('react');
class App extends React.Component {
constructor(props){
super(props)
this.state = {
items: [],
input: ""
}
this.inputChange = this.inputChange.bind(this);
this.addItems = this.addItems.bind(this);
this.remItems = this.remItems.bind(this);
}
inputChange(event){
this.setState({
input: event.target.value
})
}
addItems(){
event.preventDefault()
const temp = this.state.input
this.setState({
input: "",
items: this.state.items.concat(temp)
})
}
remItems(id){
const temp = [...this.state.items]
const updatedItems = temp.filter(item => item.id!==id)
this.setState({
items: updatedItems
})
}
render(){
return (
<div class = "w-100 p-3">
<p>Enter your item here:</p>
<form onSubmit = {this.addItems} class="form.inline">
<input class = "form.control mb-2 mr-sm-2" value = {this.state.input} onChange = {this.inputChange}/>
<button type="submit" class="btn btn-primary mb-2">Add</button>
</form>
<UnorderedList items={this.state.items} remove = {this.remItems}/>
</div>
);
}
}
class UnorderedList extends React.Component {
constructor(props){
super(props)
}
render(){
return (
<ul>
{this.props.items.map(function(item, i) {
return (<div id = "entry" class = "d-flex">
<li key={i}>{item}</li>
{console.log(i)}
<button class = 'btn btn-sm btn-primary ml-auto' onClick = {() => this.remItems}> Done! </button>
</div>)
})}
</ul>
)
}
}
I expected to be able to remove the items by id key but it gives me "Cannot read property 'props' of undefined". Thanks for your help!

Related

When I press the button I want to add many Employees, but it only leaves me one. React

Good morning, I have a question. When I press the + button, only one employee line is added and I would like it to be added as many times as I press
ReactJS component code:
class Home extends React.Component {
state = { showForm:false }
showForm = () => {
return(
<Employee />
)
}
render() {
return (
<div className='container-home'>
<div className='min-margin'>
<Employee />
{this.state.showForm ? this.showForm() : null}
<div className='container-append'>
<button onClick={() => this.setState({showForm: true})}>➕</button>
</div>
</div>
</div>
)
}
}
You just click to show and hide the input.
You need:
Add to state array: (inputs: ["Employee-0"])
state = {
showForm: false,
inputs: ["Employee-0"]
};
Add to functions
handleAddInput = e => {
e.preventDefault();
const inputState = this.state.inputs;
let inputs = inputState.concat([`Employee-${inputState.length}`]);
this.setState({
inputs
});
};
handleShowForm = e => {
e.preventDefault();
this.setState({
...this.state,
showForm: !this.state.showForm
})
}
Change the code in render
render() {
return (
<div className="App">
{this.state.showForm && <form>
{this.state.inputs.map((input, idx) => (
<Employee key={idx}/>
))}
</form>}
<button onClick={this.handleAddInput}>Add New Employee</button>
<button onClick={this.handleShowForm}>Show form</button>
</div>
);
}
Click on the buttons)
The difference options exist for doing it , but that's work you did just a flag for shown of a Component. So you are able to try followings this:
class Home extends React.Component {
state = {
employeesCount: 0,
employees: []
}
render() {
return (
<div className='container-home'>
<div className='min-margin'>
{employees.map((eNumber) => {
return <Employee key={eNumber}/>
}}
<div className='container-append'>
<button onClick={() => this.setState({
employeesCount: employeesCount + 1,
employees: [...this.state.employess , (employeesCount + 1)]
})}>➕</button>
</div>
</div>
</div>
)
}
}
Try this:
import React from "react";
const Employee = (props) => {
return(
<div>Hello I am employee number {props.number}</div>
)
}
class App extends React.Component {
constructor() {
super()
this.state = { employees: [] }
}
addEmployee() {
this.setState({
employees: [...this.state.employees, <Employee number={this.state.employees.length} />]
})
}
render() {
return (
<div>
<div className='container-append'>
<button onClick={() => this.addEmployee()}>➕</button>
</div>
{ this.state.employees.map(employee => employee) }
</div>
)
}
}
export default App;

Cannot change the state of parent component and re-render

im new to React, trying to make some simple 'Chat' app, stuck a bit in some feature.
im trying to make user list, that onClick (on one of the user) it will change the class (to active), and when hitting another user it will set the active class to the new user.
tried a lot of things, managed to make it active, but when hitting another user, the old one & the one receive the 'active' class.
here is my Parent componenet
class Conversations extends React.Component {
constructor(props) {
super(props);
this.loadConversations = this.loadConversations.bind(this);
this.selectChat = this.selectChat.bind(this);
this.state = { count: 0, selected: false, users: [] }
}
selectChat = (token) => {
this.setState({ selected: token });
}
loadConversations = (e) => {
$.get('/inbox/get_conversations', (data) => {
let r = j_response(data);
if (r) {
this.setState({ count: r['count'], users: r['data']});
}
});
}
componentDidMount = () => {
this.loadConversations();
}
render() {
return (
<div>
{this.state.users.map((user) => {
return(<User selectChat={this.selectChat} selected={this.state.selected} key={user.id} {...user} />)
})}
</div>
)
}
here is my Child componenet
class User extends React.Component {
constructor(props) {
super(props);
this.handleSelect = this.handleSelect.bind(this);
this.state = {
token: this.props.token,
selected: this.props.selected,
username: this.props.username
}
}
handleSelect = (e) => {
//this.setState({selected: e.target.dataset.token});
this.props.selectChat(e.target.dataset.token);
}
render() {
return (
<div data-selected={this.props.selected} className={'item p-2 d-flex open-chat ' + (this.props.selected == this.props.token ? 'active' : '')} data-token={this.props.token} onClick={(e) => this.handleSelect(e)}>
<div className="status">
<div className="online" data-toggle="tooltip" data-placement="right" title="Online"></div>
</div>
<div className="username ml-3">
{this.props.username}
</div>
<div className="menu ml-auto">
<i className="mdi mdi-dots-horizontal"></i>
</div>
</div>
)
}
Any help will be great...hope you can explain me why my method didnt work properly.
Thank you.
You can make use of index from map function to make element active.
Initially set selected to 0;
this.state = { count: 0, selected: 0, users: [] }
Then pass index to child component,also make sure you render your User component when you are ready with data by adding a condition.
{this.state.users.length > 0 && this.state.users.map((user,index) => {
return(<User selectChat={this.selectChat} selected={this.state.selected} key={user.id} {...user} index={index} />)
})}
In child component,
<div data-selected={this.props.selected} className={`item p-2 d-flex open-chat ${(this.props.selected === this.props.index ? 'active' : '')}`} data-token={this.props.token} onClick={() => this.handleSelect(this.props.index)}>
...
</div>
handleSelect = (ind) =>{
this.props.selectChat(ind);
}
Simplified Demo using List.

Reactjs - how to append a font-awesome icon to ref

I'm trying to use appendChild to add a fontawesome icon to a ref in react. I get the error:
Failed to execute 'appendChild' on 'Node': parameter 1 is not of type 'Node'.
Using append instead of appendChild can only add text.
class Example extends React.Component {
handle = (e) => {
e.target.appendChild('<i className="fas fa-minus-circle"/>')
}
render() {
return (
<div className="container">
<button onClick={this.handle}>CLICK HERE</button>
</div>
)
}
}
Consider changing your approach a little. You can achieve the same result you're expecting with the code below, and it's a little more common to use state and conditional JSX in React:
class Example extends React.Component {
state = { images: [] }
handle = () => {
this.setState({ images: [...images, 'fas fa-minus-circle'] });
}
render() {
return (
<div className="container">
<button onClick={this.handle}>CLICK HERE</button>
{this.state.images.map((image, index) => {
<i key={index} className={image} />
})}
</div>
);
}
}
You can create ref and attach it to the element you want to add the icon.
class Example extends React.Component{
constructor(props){
super(props);
this.iconRef= React.createRef();
}
handle = (e) => {
e.prenventDefault();
this.iconRef.current.appendChild('<i className="fas fa-minus-circle"/>')
}
render() {
return (
<div className="container">
<button ref={this.iconRef} onClick={this.handle}>CLICK HERE</button>
</div>
)
}
}

How to remove an element in reactjs by an attribute?

I want to get unknown key attribute using known ID so that i may delete corresponding div.
I tried using document.getElementById("a").getAttribute('key'); , but it isn't working. May be my concept is wrong.
class PostAdded extends Component {
constructor(props) {
super();
this.deletepost = this.deletepost.bind(this);
}
deletepost() {
let ab =document.getElementById("a").getAttribute('key');
console.log(ab)
}
render() {
return (
<div>
{ this.props.posts.map((post, i) =>
<div id="a" key={`i-${post.title}`}>
<span> <h3>{post.title}</h3><p>{post.post}</p></span>
<input type="button" value="Delete" onClick={this.deletepost}/>
</div>
) }
</div>
)
}
}
export default PostAdded;
If you were able to delete the div, that probably wouldn't end up working for you anyway because any state change would cause a re-render and it would appear again. Instead, you could keep track of your posts in state and then remove one of the posts from state in your deletepost method.
class PostAdded extends Component {
constructor(props) {
super();
this.state = {
posts: props.posts
}
this.deletepost = this.deletepost.bind(this);
}
deletepost(index) {
const newPosts = this.state.posts.splice(index)
this.setState({posts: newPosts})
}
render() {
return (
<div>
{ this.state.posts.map((post, i) =>
<div id="a" key={`i-${post.title}`}>
<span> <h3>{post.title}</h3><p>{post.post}</p></span>
<input type="button" value="Delete" onClick={() => this.deletepost(i)}/>
</div>
) }
</div>
)
}
}
export default PostAdded;

Reactjs multiple form submit

In my case, i would like to submit multiple forms in reactjs. But i have no idea on how to get the multiple form at Parent Component and submit.
here is my code:
class BulkEditor extends React.Component {
constructor(props) {
super(props);
this.state = {
items: [],
customCompanies: []
};
this.forms = [];
this.onAddChild = this.onAddChild.bind(this);
this.handleBulkSaveClick = this.handleBulkSaveClick.bind(this);
}
handleBulkSaveClick(event) {
event.preventDefault();
}
/*
* -- Add Children
*/
onAddChild() {
this.state.items.push(BulkEditorForm.defaultProps);
this.setState({
items: this.state.items
});
}
render() {
var forms = this.state.items.map(function(item, index) {
return (
<li className="list-group-item" key={index}>
<BulkEditorForm companies={this.state.customCompanies} item={item}
ref="editorform"></BulkEditorForm>
</li>
);
}.bind(this));
return (
<ul className="list-group">
{forms}
<li className="list-group-item">
<div className="btn-group btn-group-sm pull-right" role="group" aria-label="bulk-buttons">
<a href="javascript:;" className="btn btn-primary" onClick={this.onAddChild.bind(this)}>
<span className="glyphicon glyphicon-plus"></span>
</a>
<a href="javascript:;" className="btn btn-default" onClick={this.handleBulkSaveClick}>Bulk Save</a>
</div>
<div className="clearfix"></div>
</li>
</ul>
);
}
}
Here is next class
export default class BulkEditorForm extends React.Component {
constructor(props) {
super(props);
this.handleFormSubmit = this.handleFormSubmit.bind(this);
}
handleFormSubmit(event) {
event.preventDefault();
alert("Submit");
}
render() {
return (
<form action='#' method="post" onSubmit={this.handleFormSubmit}>
<button type="submit" className="btn btn-link">Save</button>
</form>
);
}
}
In your loop of rendering form list, use different ref value for each form:
<BulkEditorForm companies={this.state.customCompanies} item={item}
ref={"editorform"+index}></BulkEditorForm>
Then after all forms are rendered, access the form list by refs in your Parent Component, which means adding componentDidMount() function as follows:
class BulkEditor extends React.Component {
constructor(props) {
}
componentDidMount() {
//using basic javascript "FOR" loop ^^
for (i = 0; i < this.state.items.length; i++) {
this.forms.push(this.refs["editorform"+index]);
}
}
}
I didn't have time for testing all the code, but that's the idea! If it doesn't work yet, feel free to post here some error logs, then we may solve it together, thanks ^^

Resources