This question already has answers here:
Removing element from array in component state
(11 answers)
Closed 3 years ago.
I have a names list which contains 3 names. On clicking any 1, you can edit and save. That value updates to the list. Now I can add how many ever names I want but I am not able to delete/remove any.
This is how it looks in the beginning
On clicking a name (the whole container) it looks like this
I am able to add the new name back to the div like this
And I can add as many as I want, like this like this
Now I want to be able to click on the cross icon and remove the whole element I want. It should go be gone from the page. The other elements should take its place from top to bottom. Remove/delete functionality should be on input and the div with names in it.
Names Component (Names are extracted from this to the one below)
import Sukhdev from '../../../src/components/sukhdev';
import React from 'react';
export default { title: 'Sukhdev' };
const names = [{
firstName: "Mahatma",
lastName: "Gandhi"
}, {
firstName: "Shivaji",
lastName: "Maharaj"
}, {
firstName: "Bhagat",
lastName: "Singh"
},
]
export const sukhdev = () => {
return(
<Sukhdev names={names}/>
)
}
Parent Component
import React, { Component } from 'react';
import FirstName from './firstName';
import LastName from './lastName';
import TextArea from './textArea'
import styles from './styles';
export default class Sukhdev extends Component {
constructor(props) {
super(props);
const {names} = this.props;
const updatedNames = names.map((name) => ({...name, ...{isEditable: false}}));
this.state = {
userNames: updatedNames
}
}
inputNamesHandler = (namesIndex) => {
const updatedUserNameDetails = [...this.state.userNames];
updatedUserNameDetails[namesIndex].isEditable = true;
this.setState({userNames: updatedUserNameDetails})
}
saveButton = (inputValue, index) => {
const {userNames} = this.state;
const newNames = [...userNames];
newNames[index] = {...newNames[index], isEditable: false, firstName: inputValue, lastName: ''};
this.setState({
userNames: newNames
})
}
addChild = () => {
const createInputs = [...this.state.userNames];
createInputs.push({firstName: '', lastName: '', isEditable: true});
this.setState({
userNames: createInputs
})
}
------> // This is where the changes need to be made
deleteRow = (index) => {
const postDelete = [...this.state.userNames];
postDelete.slice(index, 1);
this.setState({
userNames: postDelete
})
}
render() {
return <div>
<h1>Names</h1>
<button onClick={this.addChild} style={styles.button}>Add New</button>
<div>
{this.state.userNames.map((nameDetails, index) => {
if(nameDetails.isEditable) {
return <div>
<TextArea clicked={(name) => this.saveButton(name, index)}/>
</div>;
} else {
return <div style={styles.namesContainer}>
<div onClick={() => this.inputNamesHandler(index)} style={styles.innerContainerComponent}>
<div style={styles.firstMargin}><FirstName firstName={nameDetails.firstName}></FirstName></div>
<div><LastName lastName={nameDetails.lastName}></LastName></div>
</div>
<img src={require('../../images/cancel.png')} style={styles.crossBtn} onClick={() => this.deleteRow(index)} />
</div>
}
})}
</div>
</div>
}
}
Textarea/Input Component
import React, { Component } from "react";
import styles from './styles'
export default class TextArea extends Component {
constructor(props) {
super(props);
this.state = {value:''}
this.handleChange = this.handleChange.bind(this);
}
handleChange(event) {
this.setState({value: event.target.value});
}
render() {
return (
<div>
<div style={styles.inputContainer}>
<input type="text" style={styles.textField} value={this.state.value} onChange={this.handleChange}></input>
<button type="submit" style={styles.saveButton} onClick={() => this.props.clicked(this.state.value)}>Save</button>
<img src={require('../../images/cancel.png')} style={styles.crossBtn} />
</div>
</div>
)
}
}
First name and last name are imported by parent component in this way
import React, {Component} from 'react';
export default class FirstName extends Component {
render() {
return <div>{this.props.firstName}</div>
}
}
Last name is also like the code given above.
slice does not modify the original array, but returns a new array with the modified values. You need to assign it to a new variable or use a different method of deleting the value.
let newArray = postDelete.slice(index, index + 1);
this.setState({
userNames: newArray
})
In fact, since slice does not mutate the array, you could simplify to this:
this.setState((prevState) => ({
userNames: prevState.userNames.slice(index, index + 1)
)})
However, to accomplish this specific task, you should use a different method like filter
this.setState((prevState) => ({
userNames: prevState.userNames.filter((v,i) => i != index)
)})
This will iterate through the array and filter out all that don't meet the condition. The first argument is the current value and the second is the index. So we only want to keep the values that do not match our index variable.
class App extends React.Component {
state = {
users: [
{
firstName: "Lionel",
lastName: "Messi"
},
{
firstName: "Cristiano",
lastName: "Ronaldo"
},
{
firstName: "Neymar",
lastName: "Jr."
},
{
firstName: "Zlatan",
lastName: "Ibrahimovic"
},
{
firstName: "Ricardo",
lastName: "Kaka"
}
]
};
updateUsers = (updatedUser, index) => {
var users = [...this.state.users];
users[index] = updatedUser;
this.setState({
users
});
};
deleteUser = index => {
var users = [...this.state.users];
users.splice(index, 1);
this.setState({
users
});
};
render() {
return (
<div>
{this.state.users.map((user, index) => {
return (
<PlayerBox
user={user}
key={Math.random()}
index={index}
updateUsers={this.updateUsers}
deleteUser={this.deleteUser}
/>
);
})}
<h1> {JSON.stringify(this.state.users)} </h1>
</div>
);
}
}
class PlayerBox extends React.Component {
state = {
editMode: false,
firstName: "",
lastName: ""
};
componentDidMount() {
const { firstName, lastName } = this.props.user;
this.setState({
firstName,
lastName
});
}
updateParent = () => {
const { index, updateUsers } = this.props;
updateUsers(
{
firstName: this.state.firstName,
lastName: this.state.lastName
},
index
);
this.setState({
editMode: false
});
};
deleteUser = () => {
const { deleteUser, index } = this.props;
this.setState({
editMode: false
});
deleteUser(index);
};
render() {
const { firstName, lastName } = this.props.user;
return this.state.editMode ? (
<div>
<div>
<input
type="text"
value={this.state.firstName}
onChange={e =>
this.setState({
firstName: e.target.value
})
}
/>
<input
type="text"
value={this.state.lastName}
onChange={e =>
this.setState({
lastName: e.target.value
})
}
/>
<button type="submit" onClick={this.updateParent}>
Save
</button>
<button onClick={this.deleteUser}> Delete </button>
</div>
</div>
) : (
<div
onClick={() =>
this.setState({
editMode: true
})
}
>
{firstName} {lastName}
</div>
);
}
}
ReactDOM.render(<App />, document.getElementById("root"));
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/16.6.3/umd/react.production.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/16.6.3/umd/react-dom.production.min.js"></script>
<div id="root"></div>
Related
I'm learning ReactJS and I want to map a json in a father component from child search bar. So I got this:
export default class Child extends Component {
constructor(props) {
super(props)
this.state = { data:[], value: '' };
this.handleSubmit = this.handleSubmit.bind(this)
this.handleChange = this.handleChange.bind(this)
}
guardar = (data) => {
this.setState({ data })
this.props.parentCallback({ data })
}
handleChange(e) {
this.setState({ value: e.target.value })
axios.get(`http://localhost:3001/api/search?query=${ e.target.value }`)
.then(( { data } ) => this.guardar(data) )
}
handleSubmit(e) {
e.preventDefault()
}
render() {
return(
<form onSubmit={this.handleSubmit}>
<input type="text"
name='searchbar'
onChange={this.handleChange}/>
</form>
)
}
}
export default class Parent extends Component {
state = {
data: [],
}
handleCallback = (childData) => {
this.setState({
data: childData
})
console.log(this.state.data);
}
render() {
const { data } = this.state
return(
<div>
<SearchBar parentCallback = {this.handleCallback}/>
<ProductCard />
{ [data].map( res => <li key={res.id}>{ res.title }</li>) }
</div>
)
}
}
Here is the result:
I want to map if even if the array is empty, in the console shows me the 50 elements only if I write more than twice in the input and I want them when I reload the page.
Beforehand thank you very much!!
Implemented writing data from the form to the array. Everything works. but now I want to implement data output when I click on " SEND ARR "
When I hang up a click on a button, respectively, the data is not displayed since in the function we access event.target.value
Please tell me how to rewrite the line so that I can display data when I click on the button? thank
home.js
import React from "react";
import "./../App.css"
export default class Home extends React.Component {
constructor() {
super()
this.state = {
count: 1,
objexm: '',
inputValue: '',
arr: []
}
this.handleClick = this.handleClick.bind(this);
this.updateInputValue = this.updateInputValue.bind(this);
}
handleClick() {
this.setState({
count: this.state.count + 1
});
};
createMarkup() {
return {
__html: this.state.inputValue
};
};
updateInputValue(event) {
let newArr = this.state.arr;
let newlist = event.target.value;
if (event.target.value) {
newArr.push(newlist)
}
this.setState({
inputValue: newArr
});
event.target.value = '';
};
render() {
return (
<div className="home-header">
<h2>{this.state.count}</h2>
<button onClick={this.handleClick}>Add</button>
<input type='text' name="names" onClick={this.updateInputValue} />
{this.state.arr.map((arrs, index) => {
return (
<li
key={index}
>{arrs}</li>
)
})}
<button>SEND ARR</button>
<ul className="qwe">
<li dangerouslySetInnerHTML={this.createMarkup()}></li>
</ul>
</div>
);
}
}
Store the data from the input into invputValue each time the input is updated and on the click of the button update the arr content with the old values (...arr) plus the current input value (this.state.inputValue) .
To make sure the old values are not deleted the arr is defined at the top of the class let arr = []. If you don't want it there you can instantiate it in the constructer which will run only once. i.e. this.arr = []
let arr = []
class Home extends React.Component {
constructor() {
super()
this.state = {
count: 1,
objexm: '',
inputValue: '',
arr: []
}
this.handleClick = this.handleClick.bind(this);
this.updateInputValue = this.updateInputValue.bind(this);
}
handleClick() {
this.setState({
count: this.state.count + 1
});
};
createMarkup() {
return {
__html: this.state.inputValue
};
};
updateInputValue = e => {
this.setState({ inputValue: e.target.value })
}
displayData = () => {
arr = [...arr ,this.state.inputValue]
this.setState({ arr, inputValue: "" })
}
clearData = () => {
this.setState({ arr: [] })
}
render() {
console.log("this.state.arr:", this.state.arr)
return (
<div className="home-header">
<h2>{this.state.count}</h2>
<button onClick={this.handleClick}>Add</button>
<input type='text' name="names" onChange={this.updateInputValue} value={this.state.inputValue} />
{this.state.arr.map((arrs, index) => {
return (
<li
key={index}
>{arrs}</li>
)
})}
<button onClick={this.displayData}>SEND ARR</button>
<button onClick={this.clearData}>CLEAR ARR</button>
<ul className="qwe">
<li dangerouslySetInnerHTML={this.createMarkup()}></li>
</ul>
</div>
);
}
}
Instead of using onClick on input, use onChange and update value in state i.e. make the input a controlled component . Post that onClick of button take the value from state and push to the array and clear the input value
export default class Home extends React.Component {
constructor() {
super()
this.state = {
count: 1,
objexm: '',
inputValue: '',
arr: []
}
this.handleClick = this.handleClick.bind(this);
this.updateInputValue = this.updateInputValue.bind(this);
}
handleClick() {
this.setState(prevState => ({
count: prevState.count + 1,
inputValue: [...prevState.inputValue, prevState.name],
name: ""
}));
};
createMarkup() {
return {
__html: this.state.inputValue
};
};
updateInputValue(event) {
let newVal = event.target.value;
this.setState({
name: newVal
});
};
render() {
return (
<div className="home-header">
<h2>{this.state.count}</h2>
<button onClick={this.handleClick}>Add</button>
<input type='text' name="names" value={this.state.name} onChange={this.updateInputValue} />
{this.state.arr.map((arrs, index) => {
return (
<li
key={index}
>{arrs}</li>
)
})}
<button>SEND ARR</button>
<ul className="qwe">
<li dangerouslySetInnerHTML={this.createMarkup()}></li>
</ul>
</div>
);
}
}
import React, { Component } from 'react';
import './App.css';
const list = [
{
title: 'React',
url: 'https://facebook.github.io/react/',
author: 'Jordan Walke',
num_comments: 3,
points: 4,
objectID: 0,
},
{
title: 'Redux',
url: 'https://github.com/reactjs/redux',
author: 'Dan Abramov, Andrew Clark',
num_comments: 2,
points: 5,
objectID: 1,
},
];
class App extends Component {
state = {
list,
text: 'abc',
searchTerm: ''
}
onDisMiss = (id) => {
const updateList = this.state.list.filter((item) => item.objectID != id)
return () => this.setState({ list: updateList })
}
onSearchChange = (event) => {
this.setState({ searchTerm: event.target.value })
}
isSearched = (searchTerm) => {
return (item) => item.title.toLowerCase().includes(searchTerm.toLowerCase())
}
render() {
const { searchTerm, list } = this.state
return (
<div>
<Search value={searchTerm}
onChange={this.onSearchChange}>Search</Search>
<Table list={list} pattern={searchTerm} onDissMiss={this.onDisMiss} />
</div>
);
}
}
class Search extends Component {
render() {
const { value, onChange, children } = this.props
return (
<div>
<form>
{children}<input type="text" onChange={onChange} value={value} />
</form>
</div>
);
}
}
class Table extends Component {
render() {
const { list, pattern, onDisMiss } = this.props
return (
<div>
{list.filter(isSearched(pattern)).map(item =>
<div key={item.objectID}>
<span><a href={item.url}>{item.title}</a></span>
<span>{item.author}</span>
<span>{item.num_comments}</span>
<span>{item.points}</span>
<span>
<button onClick={onDisMiss(item.objectID)} type="button">Dismiss</button>
</span>
</div>)
}
</div>
);
}
}
export default App;
Road to react Book The Table component related.I get undefined for the isSearched method. how can I fix it so it works correctly its from the book road to react it seems like the book has a few error which I have problems solving because am just learning react. can you help with the solution and why this problem is actually happening
You should put the isSearched method inside the Table class and not the App class
I'm building a movie character list in React.js for practice. I added a few character with components but I want to a Search filter at the top of the page. How can I add a Search Filter Component in this project?
This is my App.js;
import React from 'react';
import './App.css';
import CharacterCard from './CharacterCard.js';
function App() {
return (
<div className="characters">
<CharacterCard
name="Berlin"
imgUrl="https://m.media-amazon.com/images/M/MV5BMGE4YTZiZDgtMjQyNC00ZTA0LWI3M2UtNmFiYmUzZGRmNWU5XkEyXkFqcGdeQXVyNDg4MjkzNDk#._V1_.jpg"
quote="Right now I'm a bit busy."
/>
<CharacterCard
name="Tokyo"
imgUrl="https://m.media-amazon.com/images/M/MV5BYjY3MTk2YzUtMWRjMS00MmE1LWEyNTYtMjdkZjM1MmMwNWE4XkEyXkFqcGdeQXVyOTc1NDAzOTI#._V1_.jpg"
quote="After all, love is a good reason to make everything go wrong."
/>
<CharacterCard
name="Professor"
imgUrl="https://m.media-amazon.com/images/M/MV5BMDk0M2RlZTYtNWIxZi00ZDQ1LWI3ZTItYjRmMTA5MGIxZmE2XkEyXkFqcGdeQXVyMzQ3Nzk5MTU#._V1_.jpg"
quote="Nine hundred and eighty for million euros."
/>
<CharacterCard
name="Rio"
imgUrl="https://m.media-amazon.com/images/M/MV5BZjM3OWZhYzMtMTM4OS00YjA2LWFlYjItZTk2NTI1YmU3YTUwXkEyXkFqcGdeQXVyNDg4MjkzNDk#._V1_.jpg"
quote="I'm not a kid."
/>
<CharacterCard
name="Denver"
imgUrl="https://fotografias-nova.atresmedia.com/clipping/cmsimages01/2018/04/13/82FD1AE1-5210-466B-8346-9277965A0C4F/58.jpg"
quote="Here is a wonderful place for sleep, Dad."
/>
<CharacterCard
name="Moscow"
imgUrl="https://m.media-amazon.com/images/M/MV5BYmE0YTBhYmUtYzQ1ZC00ZGFkLWI0ZjctNmU2NDI3ODAzMWIzXkEyXkFqcGdeQXVyNDg4MjkzNDk#._V1_.jpg"
quote="It was a pleasure to get to know you."
/>
</div>
);
}
export default App;
This is my CharacterCard.js;
import React from 'react'
function CharacterCard(props) {
return(
<div className="character-card">
<img src={props.imgUrl}/>
<h3 className="character-name">{props.name}</h3>
<p>{props.quote}</p>
</div>
)
}
export default CharacterCard
You can have a component Search -
state = {
query: '',
characterArray: [// the value you want to make searchable],
characterSuggestion: [],
};
handleInputChange = () => {
this.setState({
query: this.search.value
}, () => {
if (this.state.query && this.state.query.length > 1) {
if (this.state.query.length % 2 === 0) {
this.getCharacterSuggestion();
}
}
})
};
getCharacterSuggestion = () => {
const {characterArray, query} = this.state;
this.setState({
characterSuggestion: characterArray.filter((character) => character.toLowerCase().includes(query.toLowerCase()))
})
};
<div>
<form>
<input
placeholder="Search for Character"
ref={input => this.search = input}
onChange={this.handleInputChange}
/>
<p>{this.state.query}</p>
</form>
<Suggestions characterSuggestion={characterSuggestion}/>
</div>
And in Suggestions component -
const Suggestions = (props) => {
const options = props.characterSuggestion.map((characterSuggestion, index) => (
<li key={index}>
{characterSuggestion}
</li>
));
return <ul>{options}</ul>
};
This is the basic idea to create a search component. You can make it more fancy if you want.
You can keep a value filter in your component state and use that to see if it is contained as a substring in any of the array element properties.
class App extends React.Component {
state = {
filter: "",
data: [
{
fname: "Jayne",
lname: "Washington",
email: "jaynewashington#exposa.com",
gender: "female"
},
{
fname: "Peterson",
lname: "Dalton",
email: "petersondalton#exposa.com",
gender: "male"
},
{
fname: "Velazquez",
lname: "Calderon",
email: "velazquezcalderon#exposa.com",
gender: "male"
},
{
fname: "Norman",
lname: "Reed",
email: "normanreed#exposa.com",
gender: "male"
}
]
};
handleChange = event => {
this.setState({ filter: event.target.value });
};
render() {
const { filter, data } = this.state;
const lowercasedFilter = filter.toLowerCase();
const filteredData = data.filter(item => {
return Object.keys(item).some(key =>
item[key].toLowerCase().includes(lowercasedFilter)
);
});
return (
<div>
<input value={filter} onChange={this.handleChange} />
{filteredData.map(item => (
<div key={item.email}>
<div>
{item.fname} {item.lname} - {item.gender} - {item.email}
</div>
</div>
))}
</div>
);
}
}
I have created a component that can be used for creating a new company record. A modal is opened with a form and the values are linked to the state values. In my situation, it will be possible to create more than one record of a company if the user chooses to add another company. A new company object will be pushed to the company state and the new empty form will be rendered.
This is what I've tried based on this answer:
import { Component } from 'react';
import { Modal, Header, Form, Button, Icon, Tab, Segment } from 'semantic-ui-react';
export default class CompanyCreate extends Component {
constructor(props) {
super(props);
this.state = {
company: [
{
name: '',
segment: ''
}
]
};
this.initialState = this.state;
this.handleChange = this.handleChange.bind(this);
this.handleCompanyChange = this.handleCompanyChange.bind(this);
}
handleChange = (e, { name, value }) => this.setState({ [name]: value });
handleCompanyChange = (e, { name, value }) => {
const index = this.state.company.findIndex((x) => {
return x[name] === value;
});
if (index === -1) {
console.log('error');
} else {
this.setState({
company: [
...this.state.company.slice(0, index),
Object.assign({}, this.state.company[index], value),
...this.state.company.slice(index + 1)
]
});
}
};
render() {
const { company } = this.state;
return (
<Segment>
{company.map((e, index) => (
<Form size="large" key={index}>
<Form.Group>
<Form.Input
width={6}
onChange={this.handleCompanyChange}
label="Nome"
placeholder="Nome"
name="name"
value={e.name}
required
/>
<Form.Input
width={6}
onChange={this.handleCompanyChange}
label="Segmento"
placeholder="Segmento"
name="segment"
value={e.segment}
required
/>
</Form.Group>
</Form>
))}
</Segment>
);
}
}
My problem is that I can't set the company state properly. How can you update the state in relation to the changes in the form fields?
Looking for answers, I found the package: immutability-helper. Based on this answer, the problem was solved simply and elegantly.
The solution:
import update from 'immutability-helper';
//...
this.state = {
company: [
{
name: '',
segment: ''
}
]
};
//...
handleCompanyChange = (e, { name, value, id }) => {
let newState = update(this.state, {
company: {
[id]: {
[name]: { $set: value }
}
}
});
this.setState(newState);
};
//...
render() {
const { company } = this.state;
return (
<Segment>
{company.map((e, index) => (
<Form size="large" key={index}>
<Form.Group>
<Form.Input
width={6}
onChange={this.handleCompanyChange}
label="Nome"
placeholder="Nome"
name="name"
value={e.name}
id={index}
required
/>
<Form.Input
width={6}
onChange={this.handleCompanyChange}
label="Segmento"
placeholder="Segmento"
name="segment"
value={e.segment}
id={index}
required
/>
</Form.Group>
</Form>
))}
</Segment>
);
}