I'm new to ReactJS and I'm doing a simple CRUD with ReactJS.
I want to add a new Hero to a list of Heroes in class HeroModel. However I don't know how to call the function AddHero in HeroModel.js from AddHero.js.
Here is my code:
HeroModel.js
import React, { Component } from 'react';
class HeroModel {
state = {
Heroes: [
{ Id: 1, Name: "hero1", Dob: new Date("1-1-2017"), Stocked: true, Price: 1.50 },
{ Id: 2, Name: "hero2", Dob: new Date("5-31-2018"), Stocked: false, Price: 2.50 },
{ Id: 3, Name: "hero3", Dob: new Date("2019"), Stocked: true, Price: 3.50 },
{ Id: 4, Name: "hero4", Dob: new Date("4-20-2010"), Stocked: false, Price: 4.50 },
{ Id: 5, Name: "hero5", Dob: new Date("12-31-2018"), Stocked: true, Price: 5.50 },
]
}
GetAllHeroes() {
return this.state.Heroes;
}
AddHero(name, dob, stocked, price) {
let id = this.state.Heroes.length + 1;
this.state.Heroes = [...this.state.Heroes,
{ Id: id, Name: name, Dob: dob, Stocked: stocked, Price: price }];
}
}
export default HeroModel;
AddHero.js
import React, { Component } from 'react';
import HeroModel from './HeroModel';
class AddHero extends Component {
constructor(props) {
super(props);
this.state = {
Id: 0, Name: "", Dob: new Date(), Stocked: true, Price: 0
};
}
onNameChange(e) {
this.setState({
Name: e.target.value
});
}
onDobChange(e) {
this.setState({
Dob: e.target.value
});
}
onStockedChange(e) {
this.setState({
Stocked : e.target.value
});
}
onPriceChange(e) {
this.setState({
Price : e.target.value
});
}
onSave(e){
//----I get stuck here-----
// (new HeroModel).AddHero(
// this.state.Name, this.state.Dob,
// this.state.Stocked, this.state.Price);
}
render() {
return (
<div>
<h1>Add hero</h1>
<form>
<div className="form-group">
<label>Name </label>
<input type="text" onChange={this.onNameChange.bind(this)}></input>
</div>
<div className="form-group">
<label>Dob </label>
<input type="date" onChange={this.onDobChange.bind(this)} defaultValue={new Date().toISOString().substr(0, 10)}></input>
</div>
<div className="form-group">
<label>Stocked </label>
<input type="checkbox" onChange={this.onStockedChange.bind(this)} ></input>
</div>
<div className="form-group">
<label>Price </label>
<input type="text" onChange={this.onPriceChange.bind(this)}></input>
</div>
<button onClick={this.onSave.bind(this)}>Save</button>
</form>
</div>
);
}
}
export default AddHero;
Please help me to add a new Hero to a list of Heroes in HeroMode.js.
I comment in function onSave(e) where I get stuck.
Thank you in advanced.
Edit, for redux implementation, see this sandbox: https://codesandbox.io/s/simple-reactredux-flow-l9oq4
To call a function defined in another function, you need to pass it down as a property. This creates a Parent-Child relationship.
Let's consider the following:
Your Parent-component holds the list of Heroes in its state.
Your Parent-component has a function that lets you add more heroes.
We will call it createNewHero()
Your Parent-component passes createNewHero() down as a prop to the
child, AddHero
AddHero holds input data in its state, we will pass that data as an object to the prop, createNewHero(). Effectively passing the data upwards.
WhencreateNewHero() is called, its execution context is bound to
the parent component. So when this.setState() is executed, the keyword, this points to HeroModel's state.
When complete, we add a single Hero to the list, thus changing the
state, causing our component to re-render.
Checkout the following sandbox to see this in action: https://codesandbox.io/s/frosty-dawn-l9oq4
HeroModel (Parent)
import React from "react";
import ReactDOM from "react-dom";
import AddHero from "./AddHero";
import "./styles.css";
class HeroModel extends React.Component {
state = {
Heroes: [
{
Id: 1,
Name: "hero1",
Dob: new Date("1-1-2017"),
Stocked: true,
Price: 1.5
},
{
Id: 2,
Name: "hero2",
Dob: new Date("5-31-2018"),
Stocked: false,
Price: 2.5
},
{
Id: 3,
Name: "hero3",
Dob: new Date("2019"),
Stocked: true,
Price: 3.5
},
{
Id: 4,
Name: "hero4",
Dob: new Date("4-20-2010"),
Stocked: false,
Price: 4.5
},
{
Id: 5,
Name: "hero5",
Dob: new Date("12-31-2018"),
Stocked: true,
Price: 5.5
}
]
};
createNewHero = newHero => {
this.setState({
...this.state,
Heroes: [...this.state.Heroes, newHero]
});
};
renderHeros = () => {
const { Heroes } = this.state;
return Heroes.map(hero => {
return <div>{hero.Name}</div>;
});
};
render() {
return (
<div>
{this.renderHeros()}
<AddHero createNewHero={this.createNewHero} />
</div>
);
}
}
export default HeroModel;
AddHero (Child)
import React from "react";
class AddHero extends React.Component {
constructor(props) {
super(props);
this.state = {
Id: 0,
Name: "",
Dob: new Date(),
Stocked: true,
Price: 0
};
}
onSave = e => {
e.preventDefault();
const { Id, Name, Stocked, Price } = this.state;
const newHero = {
Id: Id,
Name: Name,
Dob: new Date(),
Stocked: Stocked,
Price: Price
};
this.props.createNewHero(newHero);
};
onChange = e => {
this.setState({
[e.target.name]: e.target.value
});
};
render() {
return (
<div>
<h1>Add hero</h1>
<form>
<div className="form-group">
<label>Name </label>
<input type="text" onChange={this.onChange} name="Name" />
</div>
<div className="form-group">
<label>Dob </label>
<input
type="date"
onChange={this.onChange}
defaultValue={new Date().toISOString().substr(0, 10)}
name="Dob"
/>
</div>
<div className="form-group">
<label>Stocked </label>
<input type="checkbox" onChange={this.onChange} name="Stocked" />
</div>
<div className="form-group">
<label>Price </label>
<input type="text" onChange={this.onChange} name="Price" />
</div>
<button onClick={this.onSave}>Save</button>
</form>
</div>
);
}
}
export default AddHero;
Related
I have two input fields here and I would like to create a new cards object by clicking submit. I got as far as displaying the object in the dev tools, but i can't manage to display this data from the input fields as a new card.
I suspect it has something to do with formdata, append and state management.
Does anyone have an idea how this works?
My App.js:
import "./App.css";
import Card from "./components/Card";
import Header from "./components/Header";
import CardComponents from "./components/CardComponents";
import CreateForm from "./components/Form";
export default function App() {
const colorCard = [
{
name: "Card1",
id: "234",
colorCode: "#ccc",
},
{
name: "Card2",
id: "2",
colorCode: "#4c6ef5",
},
{
name: "Card3",
id: "3",
colorCode: "#82c91e",
},
{
name: "Card4",
id: "4",
colorCode: "#12b886",
},
{
name: "Card5",
id: "5",
colorCode: "#00FFFF",
},
{
name: "Card6",
id: "7",
colorCode: "#9FE2BF",
},
{
name: "Card8",
id: "5",
colorCode: "#DE3163",
},
{
name: "Card9",
id: "5",
colorCode: "#50C878",
},
{
name: "Card10",
id: "5",
colorCode: "#40E0D0",
},
];
return (
<main>
<Header />
<CreateForm />
<ul>
{colorCard.map((card) => (
<CardComponents
key={card.id}
color={card.colorCode}
name={card.name}
/>
))}
</ul>
</main>
);
}
Form.js component:
import { useState } from "react";
const CreateForm = () => {
const [value, setValue] = useState("");
const [code, setCode] = useState("");
const submitHandler = (event) => {
event.preventDefault();
const newColorBox = { value, code };
console.log(newColorBox);
};
return (
<form onSubmit={submitHandler}>
<h2>Add Card</h2>
<input
type="text"
required
placeholder="Name your Card"
value={value}
onChange={(event) => setValue(event.target.value)}
></input>
<br></br>
<input
type="text"
placeholder="Name your Color Code"
required
value={code}
onChange={(event) => setCode(event.target.value)}
></input>
<br></br>
<button type="submit">Submit your Card</button>
<p> CardName: {value}</p>
<p>ColorCode: {code}</p>
</form>
);
};
export default CreateForm;
1 Add:
const [cards,setCards]=useState(/*you car insert your initial data in app.js*/)
pass setCards() to form.js and call it inside of submitHandler.
Goal:
Display react bootstrap's modal when you press the button 'Open Modal'
Problem:
I do not know how to make it to show bootstrap's modal when I press the button 'Open Modal'
What part am I missing?
Stackblitz:
https://stackblitz.com/edit/react-bootstrap-examples-suktpo?
Info:
*I'm newbie in Reactjs
Thank you!
index.html
<!-- Latest compiled and minified CSS -->
<link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.7/css/bootstrap.min.css" integrity="sha384-BVYiiSIFeK1dGmJRAkycuHAHRg32OmUcww7on3RYdg4Va+PmSTsz/K68vbdEjh4u" crossorigin="anonymous">
<!-- Optional theme -->
<link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.7/css/bootstrap-theme.min.css" integrity="sha384-rHyoN1iRsVXV4nD0JutlnGaslCJuC7uwjduW9SVrLvRYooPp2bWYgmgJQIXwl/Sp" crossorigin="anonymous">
<div id="root"></div>
index.js
import React, { Component } from 'react';
import { render } from 'react-dom';
import './style.css';
import DisplayModalContent from './DisplayModalContent';
import { Modal, Button } from 'react-bootstrap';
class App extends Component {
constructor() {
super();
this.state = {
openItem: null,
items: [
{
firstName: 'Josef',
lastName: 'Anderson',
key: 'josef.anderson',
startYear: 2021,
startMonth: 2
},
{
firstName: 'Jim',
lastName: 'West',
key: 'jim.west',
startYear: 2020,
startMonth: 3
},
{
firstName: 'Joe',
lastName: 'West',
key: 'joe.west',
startYear: 1998,
startMonth: 10
}
],
firstName: '',
lastName: ''
};
}
handleOpenModal = openItem => {
this.setState({ openItem });
};
handleCloseModal = () => {
this.setState({ openItem: null });
};
handleOpenItemValue = e => {
let { name, value } = e.target;
this.setState({
openItem: { ...this.state.openItem, [name]: value }
});
};
handleSubmit = () => {
console.log(document.getElementsByName('startMonth')[0].value);
alert(
JSON.stringify({
test: document.getElementsByName('startMonth')[0].value
})
);
};
render() {
const { items, openItem } = this.state;
return (
<div>
<table border="1">
<thead>
<tr>
<th>First Name</th>
<th>Last Name</th>
<th />
</tr>
</thead>
<tbody>
{items.map(item => {
const { firstName, lastName, key } = item;
return (
<tr key={key}>
<td>{firstName}</td>
<td>{lastName}</td>
<td>
<button onClick={() => this.handleOpenModal(item)}>
Open Modal
</button>
</td>
</tr>
);
})}
</tbody>
</table>
<DisplayModalContent item={openItem} />
</div>
);
}
}
render(<App />, document.getElementById('root'));
DisplayModalContent.js
import React, { Component } from 'react';
import { render } from 'react-dom';
import './style.css';
import { Modal, Button } from 'react-bootstrap';
const options = [
{ value: 1, label: 'Jan' },
{ value: 2, label: 'Feb' },
{ value: 3, label: 'Mars' },
{ value: 4, label: 'April' },
{ value: 5, label: 'May' },
{ value: 6, label: 'June' },
{ value: 7, label: 'July' },
{ value: 8, label: 'August' },
{ value: 9, label: 'Sept' },
{ value: 10, label: 'Oct' },
{ value: 11, label: 'Nov' },
{ value: 12, label: 'Dec' }
];
class DisplayModalContent extends Component {
constructor() {
super();
this.state = {
openItem: null,
firstName: '',
lastName: ''
};
}
componentDidUpdate(s) {
if (JSON.stringify(this.props) !== JSON.stringify(s)) {
this.setState({ openItem: this.props.item });
}
}
handleOpenModal = openItem => {
this.setState({ openItem });
};
handleCloseModal = () => {
this.setState({ openItem: null });
};
handleOpenItemValue = e => {
let { name, value } = e.target;
this.setState({
openItem: { ...this.state.openItem, [name]: value }
});
};
handleSubmit = () => {
console.log(document.getElementsByName('startMonth')[0].value);
alert(
JSON.stringify({
test: document.getElementsByName('startMonth')[0].value
})
);
};
hideShowModal = () => {
this.setState({ isModalOpen: !this.state.isModalOpen });
};
render() {
const { items, openItem } = this.state;
return (
<div>
{openItem !== null && (
<div isOpen={true}>
<Button variant="primary" onClick={() => this.hideShowModal()}>
Click to hide/show
</Button>
<Modal
show={this.state.isModalOpen}
onHide={() => this.hideShowModal()}
>
<Modal.Header closeButton>
<Modal.Title>This is modal title</Modal.Title>
</Modal.Header>
<Modal.Body>
<p>
First Name:
<br />
<input
type="text"
id="firstName"
name="firstName"
value={openItem.firstName}
onChange={e => this.handleOpenItemValue(e)}
/>
<input
type="text"
id="lastName"
name="lastName"
value={openItem.lastName}
onChange={e => this.handleOpenItemValue(e)}
/>
</p>
</Modal.Body>
<Modal.Footer>
<Button variant="secondary">CLOSE</Button>
<Button variant="primary">SAVE</Button>
</Modal.Footer>
</Modal>
</div>
)}
</div>
);
}
}
export default DisplayModalContent;
you don't have to use the state in the two components, just define the state in the parent component and pass it as a prop to the child component.
index.js
import React, { Component } from "react";
import DisplayModalContent from "./DisplayModalContent";
import { Modal, Button } from "react-bootstrap";
const items = [
{
firstName: "Josef",
lastName: "Anderson",
key: "josef.anderson",
startYear: 2021,
startMonth: 2
},
{
firstName: "Jim",
lastName: "West",
key: "jim.west",
startYear: 2020,
startMonth: 3
},
{
firstName: "Joe",
lastName: "West",
key: "joe.west",
startYear: 1998,
startMonth: 10
}
];
class App extends Component {
constructor() {
super();
this.state = {
open: false,
openItem: null,
items: [],
firstName: "",
lastName: ""
};
}
componentDidMount() {
this.setState({ items });
}
handleOpenModal = (openItem) => {
this.setState({ openItem, open: true });
};
handleCloseModal = () => {
this.setState({ open: false });
};
handleOpenItemValue = (e) => {
let { name, value } = e.target;
this.setState({
openItem: { ...this.state.openItem, [name]: value }
});
};
handleSubmit = (key) => {
const { openItem, items } = this.state;
const updatedItems = items.filter((i) => i.key !== key);
this.setState({
open: false,
items: [...updatedItems, openItem]
});
// console.log(document.getElementsByName("startMonth")[0].value);
// alert(
// JSON.stringify({
// test: document.getElementsByName("startMonth")[0].value
// })
// );
};
render() {
const { items, openItem, open } = this.state;
return (
<div>
<table border="1">
<thead>
<tr>
<th>First Name</th>
<th>Last Name</th>
<th />
</tr>
</thead>
<tbody>
{items &&
items.map((item) => {
const { firstName, lastName, key } = item;
return (
<tr key={key}>
<td>{firstName}</td>
<td>{lastName}</td>
<td>
<button onClick={() => this.handleOpenModal(item)}>
Open Modal
</button>
</td>
</tr>
);
})}
</tbody>
</table>
<DisplayModalContent
item={openItem}
isOpen={open}
onClose={this.handleCloseModal}
onSubmit={this.handleSubmit}
handleOpenItemValue={(e) => this.handleOpenItemValue(e)}
/>
<br />
<div> {JSON.stringify(items)} </div>
</div>
);
}
}
export default App;
DisplayModalContent.js
import React, { Component } from "react";
import { Modal, Button } from "react-bootstrap";
const options = [
{ value: 1, label: "Jan" },
{ value: 2, label: "Feb" },
{ value: 3, label: "Mars" },
{ value: 4, label: "April" },
{ value: 5, label: "May" },
{ value: 6, label: "June" },
{ value: 7, label: "July" },
{ value: 8, label: "August" },
{ value: 9, label: "Sept" },
{ value: 10, label: "Oct" },
{ value: 11, label: "Nov" },
{ value: 12, label: "Dec" }
];
class DisplayModalContent extends Component {
render() {
const { item, isOpen, onClose, handleOpenItemValue, onSubmit } = this.props;
return (
<div>
<div>
<Modal show={isOpen} onHide={onClose}>
<Modal.Header closeButton>
<Modal.Title>This is modal title</Modal.Title>
</Modal.Header>
<Modal.Body>
<p>
First Name:
<br />
<input
type="text"
id="firstName"
name="firstName"
value={item && item.firstName}
onChange={(e) => handleOpenItemValue(e)}
/>
<input
type="text"
id="lastName"
name="lastName"
value={item && item.lastName}
onChange={(e) => handleOpenItemValue(e)}
/>
</p>
</Modal.Body>
<Modal.Footer>
<Button variant="secondary" onClick={onClose}>
CLOSE
</Button>
<Button variant="primary" onClick={() => onSubmit(item.key)}>
SAVE
</Button>
</Modal.Footer>
</Modal>
</div>
</div>
);
}
}
export default DisplayModalContent;
Live working Demo
I was reviewing your code and I see you're using this.state.isModalOpen to show the modal, but in the DisplayModalContent component that state is never updated when you're receiving the props from your index. So, the easy fix for that is update isModalOpen when you are receiving the props. However, from my experience I would recommend do not use a new state to handle the modal open state in the DisplayModalContent component, you can use it directly from the props:
<DisplayModalContent
item={openItem}
isOpen={!!openItem}
onClose={this.handleCloseModal}
/>
and in your DisplayModalContent component, you can replace any local state and in order to use the component props:
...
<Modal
show={this.props.isOpen}
onHide={this.props.onClose}
>
...
By doing this your code will be more simple and readable.
I have a list of li elements that is being passed down to one of my components. These are being rendered in the component. I have a search bar in the same component. I want to be able to only render the items that match what is written down in the search bar. This is what my component looks like.
import React, {Component} from 'react'
import {NavLink} from 'react-router-dom'
import LikeBtn from './LikeBtn'
class SearchForm extends Component {
constructor(props) {
super(props)
this.state = {
search: '',
newList: []
}
}
handleChange = (e) => {
this.setState({
[e.target.name]: e.target.value,
})
}
render() {
let list = this.props.listProp.map(item => <li className="listItem" key={item.id}><NavLink style={{ color: 'white' }} to={`activities/${item.id}`}>{item.name}</NavLink><LikeBtn /></li>)
let newList = list.filter(item => item.innerText === this.state.search)
console.log(newList)
return (
<>
<input type="text" name='search' onChange={this.handleChange}/>
<ul>
{list}
</ul>
</>
)
}
}
export default SearchForm
I don't know how to get that filtered out so that I can render the items. I tried doing innerText but since I have a LikeBtn component in the li element my filter doesn't work. How else would I be able to implement this? Are there more efficient ways of doing this?
You need to filter your data and not grab something you've already rendered on screen.
render() {
let filteredList = this.state.search
? this.props.listProp.filter((item) =>
item.name.includes(this.state.search),
)
: this.props.listProp;
return (
<>
<input type="text" name="search" onChange={this.handleChange} />
<ul>
{filteredList.map((item) => (
<li className="listItem" key={item.id}>
<NavLink style={{ color: 'white' }} to={`activities/${item.id}`}>
{item.name}
</NavLink>
<LikeBtn />
</li>
))}
</ul>
</>
);
}
There is also a snippet below that you can run:
class SearchForm extends React.Component {
constructor(props) {
super(props);
this.state = {
search: '',
};
}
handleChange = (e) => {
this.setState({
[e.target.name]: e.target.value,
});
};
render() {
let filteredList = this.state.search
? this.props.listProp.filter((item) =>
item.name.toLowerCase().includes(this.state.search.toLowerCase())
)
: this.props.listProp;
return (
<React.Fragment>
<input type="search" name="search" autocomplete="off" onChange={this.handleChange} />
<ul>
{filteredList.map((item) => (
<li className="listItem" key={item.id}>
<a href={`activities/${item.id}`}>{item.name}</a>
<button>Like</button>
</li>
))}
</ul>
</React.Fragment>
);
}
}
const data = [
{
id: 1,
name: 'Congress, The',
},
{
id: 2,
name: 'Glen or Glenda',
},
{
id: 3,
name: "Don't Drink the Water",
},
{
id: 4,
name: 'Blind',
},
{
id: 5,
name: 'Sirocco',
},
{
id: 6,
name: 'Sunset Strip',
},
{
id: 7,
name: 'Better Living',
},
{
id: 8,
name: '4:44 Last Day on Earth',
},
{
id: 9,
name: 'The Heavy',
},
{
id: 10,
name: 'Dr. Who and the Daleks',
},
{
id: 11,
name: 'Legend of Hell House, The',
},
{
id: 12,
name: 'Exit Humanity',
},
{
id: 13,
name: 'Young in Heart, The',
},
{
id: 14,
name: 'Soul Kitchen',
},
{
id: 15,
name: 'Accattone',
},
];
ReactDOM.render(<SearchForm listProp={data} />, document.querySelector('#root'));
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/17.0.1/umd/react.production.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/17.0.1/umd/react-dom.production.min.js"></script>
<div id="root"></div>
You can move this part
let list = this.props.listProp.map(item => <li className="listItem" key={item.id}><NavLink style={{ color: 'white' }} to={`activities/${item.id}`}>{item.name}</NavLink><LikeBtn /></li>)
to the return and filter the listProp on handleChange.
You can refer to this codeSandbox here for a working sample
I am working on an old app and I am trying to restructure the form that will be used to perform crud on my backend. I decided to make the input reusable, however, I have an issue that when I type into one box I type into all of them, that is, they are all the event target at once! how do I make it so only the input in focus will be typed into?
//update.js
import React, { Component } from "react";
import axios from "../../node_modules/axios";
import Input from "./input";
import "./update.css";
class Update extends Component {
constructor(props) {
super(props);
this.state = {
_id: "",
brand: "",
name: "",
price: "",
imageLink: "",
productLink: "",
category: "",
productType: "",
};
this.handleChange = this.handleChange.bind();
this.onSubmit = this.onSubmit.bind();
}
handleChange = (evt) => {
this.setState({ [evt.target.name]: evt.target.value });
};
onSubmit = (evt) => {
evt.preventDefault();
console.log(this.state);
axios
.put(
`https://makeupinfo.herokuapp.com/product${this.state._id}`,
this.state
)
.then((res) => {
console.log(res);
})
.then((err) => {
console.log(err);
});
};
render() {
const {
_id,
brand,
name,
price,
imageLink,
productLink,
productCategory,
productType,
} = this.state;
const info = [
{ name: "_id", placeholder: "product ID", value: _id },
{ name: "brand", placeholder: "brand", value: brand },
{ name: "name", placeholder: "product name", value: name },
{ name: "price", placeholder: "price", value: price },
{ name: "image-link", placeholder: "image link", value: imageLink },
{ name: "product-link", placeholder: "product link", value: productLink },
{
name: "product category",
placeholder: "product category",
value: productCategory,
},
{ name: "product type", placeholder: "product type", value: productType },
];
return (
<div>
<h2 className='links'>Search by ID and update</h2>
<div className='form-container'>
<Input props={info}></Input>
</div>
</div>
);
}
}
export default Update;
and the child component:
import React from "react";
class Input extends React.Component {
constructor(props) {
super(props);
this.state = {
value: "",
};
this.handleChange = this.handleChange.bind(this);
}
handleChange = (e) => {
e.preventDefault();
this.setState({ value: e.target.value });
console.log(this.state.value);
};
render() {
const data = this.props.props;
const dataArray = data.map((item) => (
<input
className='form-control form-control-sm'
name={item.name}
type='text'
placeholder={item.placeholder}
value={this.state.value}
onChange={this.handleChange}
key={item.name}
/>
));
return <div>{dataArray}</div>;
}
}
export default Input;
All your inputs share the same state.value from the Input component. So when you edit one, you edit all of them. Your Input component should only render one input, and you should move info.map to the Update component.
Right now you call map inside Input which generate all the inputs with one shared value. If you do the map in your Update component, you will render one input for each Input, and they will all have their own value, so no more side effect.
In the code below, I am trying to remove a person from what will eventually be an org chart when the delete button next to their name is clicked. At the moment, nothing happens. The closest I've come is all 5 people being deleted when any one of the delete buttons is clicked, but I only want the one person deleted who's button is clicked. I feel like I'm making more of a JS error than a React error.
See the full code sandbox here.
Any help would be appreciated, thank you!
import React from "react";
import { Component } from "react";
const list = [
{
name: "Person 1",
phone: "123-4567",
itemId: 11
},
{
name: "Person 2",
phone: "123-4567",
itemId: 12
},
{
name: "Person 3",
phone: "123-4567",
itemId: 23
},
{
name: "Person 4",
phone: "123-4567",
itemId: 34
},
{
name: "Person 5",
phone: "123-4567",
itemId: 45
}
];
class Entry extends Component {
constructor(props) {
super(props);
this.state = {
list: list
};
this.handleClick = this.handleClick.bind(this);
}
handleClick(e) {
this.setState({
list: this.state.list.filter(function(person) {
return person !== e.target.value;
})
});
}
render() {
return (
<div>
{this.state.list.map(item =>
<tr key={item.itemId}>
<td>
{item.name}
</td>
<td>
{item.phone}
</td>
<td>
<a className="delete" onClick={this.handleClick} />
</td>
</tr>
)}
</div>
);
}
}
export default Entry;
Your click event has no value, you can pass the itemId:
onClick={() => this.handleClick(item.itemId)}
Then your handleClick should look like:
handleClick(itemId) {
this.setState({
list: this.state.list.filter(function(person) {
return person.itemId !== itemId;
})
});
}
https://codesandbox.io/s/mo2l8z7469
Both the above solution violates one of the best practices or I should say essential practices of react, that we should use property initializer syntax, which is passing the function defined above instead of passing an arrow function inside prop you can read it here in last paragraph of https://facebook.github.io/react/docs/handling-events.html
class Entry extends Component {
/* your handler method */
handleDelete(itemId){
return () => {
this.setState({
/* logic to filter deleted item from old state */
});
}
}
/* render method */
render() {
return (
<div>
{/* passing onDelete callback */}
<a className="delete" onClick={this.handleClick(item.id)} />
</div>
)
}
}
//import React from 'react';
//import ReactDOM from 'react-dom';
const list = [
{
name: "Person 1",
phone: "123-4567",
itemId: 11
},
{
name: "Person 2",
phone: "123-4567",
itemId: 12
},
{
name: "Person 3",
phone: "123-4567",
itemId: 23
},
{
name: "Person 4",
phone: "123-4567",
itemId: 34
},
{
name: "Person 5",
phone: "123-4567",
itemId: 45
}
];
class Entry extends React.Component {
constructor(props) {
super(props);
this.state = {
list: list
};
this.handleClick = this.handleClick.bind(this);
}
handleClick(item) {
let filterList = this.state.list.filter((user) => {
if(user.itemId === item.itemId) {
return false;
}
return true;
})
this.setState({
list: filterList
});
}
render() {
return (
<div>
<table>
<tbody>
{this.state.list.map(item =>
<tr key={item.itemId}>
<td>
{item.name}
</td>
<td>
{item.phone}
</td>
<td>
<button className="delete" onClick={() => this.handleClick(item)} >Del</button>
</td>
</tr>
)}
</tbody>
</table>
</div>
);
}
}
ReactDOM.render(
<Entry />,
document.getElementById('app')
);
delete
<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="app"></div>