I am making a form that allows a user to build a quiz (here's my code so far)
var uuid = require("uuid-v4");
// Generate a new UUID
var myUUID = uuid();
// Validate a UUID as proper V4 format
uuid.isUUID(myUUID); // true
var questionNum = 0;
class App extends Component {
constructor(props) {
super(props);
this.state = {
key: uuid(),
title: "",
author: "",
questions: [],
answers: []
};
this.handleChange = this.handleChange.bind(this);
this.addQuestion = this.addQuestion.bind(this);
}
componentDidMount() {
// componentDidMount() is a React lifecycle method
this.addQuestion();
}
handleChange(event) {
const target = event.target;
const value = target.type === "checkbox" ? target.checked : target.value;
const name = target.name;
this.setState({
[name]: value
});
}
/**
* It's probably better to structure your questions like this:
* this.state.questions: [{
* question: 'How are you?',
* answers: ['Good', 'Great', 'Awful'],
* // correctAnswer: 'Great'
* },
* {
* question: 'What is your name?',
* answers: ['Toby', 'Marco', 'Jeff'],
* // correctAnswer: 'Jeff'
* }];
*
* This allows you to keep better track of what questions
* have what answers. If you're making a 'quiz' type structure,
* you could additionally add a `correctAnswer` property.
*/
addQuestion() {
questionNum++;
this.setState(previousState => {
const questions = [...previousState.questions, "question", "hi"];
const answers = [...previousState.answers];
for (var i = 0; i < 4; i++) {
answers.push({
answerChoice: "",
key: uuid()
});
}
return { questions, answers };
});
console.log(
this.state.answers,
this.state.questions,
questionNum,
this.state.title,
this.state.author
);
}
render() {
return (
<div className="App">
<div>
<header className="App-header">
<img src={logo} className="App-logo" alt="logo" />
<h1 className="App-title">Quiz Form 2.0</h1>
</header>
<p className="App-intro">
To get started, edit <code>src/App.js</code> and save to reload.
</p>
</div>
<div>
<form>
<div className="Intro">
Give your Quiz a title:{" "}
<input
type="text"
value={this.state.title}
onChange={this.handleChange}
name="title"
/>
<br />
Who's the Author?{" "}
<input
type="text"
value={this.state.author}
onChange={this.handleChange}
name="author"
/>
<br />
<br />
</div>
<div className="questions">
Now let's add some questions... <br />
{// This is where we loop through our questions to
// add them to the DOM.
this.state.questions.map(question => {
return <div>{question}</div>;
})
// This is what it would look like for the structure
// I proposed earlier.
// this.state.questions.map((question) {
// return (
// <div>{question.quesion}</div>
// {
// question.answers.map((answer) => {
// return (<div>{answer}</div>);
// })
// }
// );
// })
// This would output all questions and answers.
}
</div>
</form>
<button onClick={this.addQuestion}>Add Question</button>
</div>
</div>
);
}
}
export default App;
And I have come to the point where I want to try and be able to "Remove" a question (using a button). What my code does now is it adds objects to an array, and I have got that figured out. But now I am trying to remove Items from the array. I was thinking "ok just remove the last question" but realistically users will want to remove any of their questions. I was just curious if anyone had some tips for this, I really don't know how to start.
If you want the user to be able to remove any question, add an onClick to the question div (or a child of the div - remember to move the onClick). The callback for that can accept an index, which refers to the element in the list to remove.
Example:
class App extends Component {
constructor(props) {
super(props)
this.removeItem = this.removeItem.bind(this)
}
removeItem (index) {
this.setState(({ questions }) => {
const mQuestions = [ ...questions ]
mQuestions.splice(index, 1)
return { questions: mQuestions }
})
}
render () {
return (
<div>
...
{ this.state.questions.map((question, index) => {
return <div onClick={ () => this.removeItem(index) }>{ question }</div>
}) }
</div>
)
}
}
This isn't so much a react question as a JavaScript question. Since your questions are stored in your react state it will update the DOM when that state is modified. Simply removing the value from the array using this.setState() will suffice.
You have a few options for removing values from your array. The main thing to keep in mind here is to make sure you don't modify the actual array but rather replace it with a new instance of an array. Modifying the array directly will not trigger updates and goes against the general principles of react state. For example, using
Array.prototype.splice() would modify your original array.
(Array.prototype.splice docs)
In JavaScript, primatives such as strings and numbers are passed by value however objects such as arrays, sets, or generic JavaScript objects are passed by reference. This means that by assigning an object to a new variable you will now have two variables pointing at the same object.
const foo = [1];
const bar = foo;
console.log(foo); // [1]
console.log(bar); // [1]
foo.push(2);
console.log(foo); // [1, 2]
console.log(bar); // [1, 2]
One common way to get around this is by using the ES6 spread notation (Spread operator docs) to spread the values into a new array. const bar = [...foo] would return a copied array pointing to a separate object. Applying this to your question, you could do const q = [...this.state.questions] and then modify q using q.splice(index, 1) and assign that to your state using this.setState(). There are obviously other options to removing items from an array and I suppose it is not a given that you know the index of the array. In this case, tools such as Array.prototype.find() or Array.prototype.findIndex() are helpful or you could use a JavaScript Map object (Map docs) in place of your array to eliminate the need for indexes while maintaining question order. All of these options are equally valid so I'll leave it to you to determine how you want to go about it.
In order to actually trigger the deletion you will need to have some sort of user control on the page. You can do this with click listeners on elements, specific buttons included in the HTML for each question, or maybe even a dropdown menu elsewhere for deletion. The end result will be that the element the user interacts with will maintain a unique ID so that when it activates the callback function you can determine which question it is that should be deleted. This ID will be stored in the first argument of your function. In most examples this will be named "event".
Related
I want to mount a component which will mount children components based on how many times a user hits a + ingredient icon as shown here (this screenshot shows one hardcoded to render)
I want to achieve this by setting an ingredients state to initially be an empty array
const [ingredients, setIngredients] = useState([]);
And then have the onClick action of the plus icon add an empty object to this array, while preserving any objects that have been added/modified
const addElementToArray = () => {
setIngredients((prevIngredients) => [
...prevIngredients, {}
]);
}
However, the current behavior is a bit mysterious to me.
When console.log(ingredients) it is initially an empty array, great
However, when I click the + icon once it transforms to:
[{…}, [object Object]: {…}]
and then I believe back to an empty object
And finally, when I click it again the component crashes with the error:
Uncaught TypeError: prevIngredients is not iterable
I'm unsure how to successfully get the functionality I want, any help is appreciated.
Full file below:
const RecipeCreate = props => {
const navigate = useNavigate();
const [ ingredients, setIngredients ] = useState([]);
const [ recipeTitle, setRecipeTitle ] = useState('');
useEffect(() => {
console.log(ingredients)
renderIngredientComponents()
}, [ingredients])
//will render IngredientDetailsInput for each obj in ingredients
//and will be updated using callbacks from that child component
const renderIngredientComponents = () => {
if (ingredients.length > 0) {
return ingredients.map((index, ingredient) => {
return <IngredientDetailsInput
key={index}
position={index}
updateIngredientArray={updateIngredientArray}
removeIngredient={removeIngredient}
/>
})
}
}
//failing function
const addElementToArray = () => {
setIngredients((prevIngredients) => [
...prevIngredients, {}
]);
}
return (
<div>
<div>
<form onSubmit={e => handleSubmit(e)}>
<div>
<label>Recipe Title</label>
<input
type="text"
name="recipeTitle"
value={recipeTitle}
onChange={e => setRecipeTitle(e.target.value)}/>
</div>
<div>
<p>Ingredients</p>
{renderIngredientComponents()}
<div>
<p onClick={()=> addElementToArray()}>+ ingredient</p>
</div>
</div>
<button type="submit">Submit</button>
</form>
</div>
</div>
)
}
export default RecipeCreate;
There are a number of problems here:
setIngredients(ingredients.splice(ingredients[position], 1))
splice mutates the array it's called on - but ingredients is stateful, and state should never be mutated. Mutating state often results in confusing and unpredictable behavior, and should not be done in React.
splice returns the removed element, not the mutated array
You need
setIngredients(ingredients.filter((_, i) => i !== position));
Also
setIngredients(ingredients[position] = details)
I know the comment says it's not implemented yet, but it's also causing a bug - you're passing details to setIngredients, so that it's no longer an array.
setIngredients(ingredients[position] = details)
is equivalent to
ingredients[position] = details;
setIngredients(details)
I'm not sure what logic you want there, but it's causing problems - remove that part entirely until you implement it properly, else it'll mess up your state.
I am new in react world. I have this example code here where with the deletePersonHandler method i am deleting some objects from the array.
class App extends Component {
state = {
persons: [
{ name: "peter", age: 24 },
{ name: "john", age: 25 },
{ name: "jake", age: 30 }
],
showPersons: true
}
deletePersonHandler = index => {
const persons = this.state.persons;
persons.splice(index,1);
this.setState({persons:persons})
}
togglePersonsHandler = () => {
this.setState({ showPersons: !this.state.showPersons })
}
render() {
let persons = null;
if (this.state.showPersons) {
persons = (
<div>
{this.state.persons.map((person, index) => {
return <Person
click={() => this.deletePersonHandler(index)}
name={person.name}
age={person.age}
key={index}
/>
})}
</div>
);
}
return (
<div className="App">
{persons}
<button onClick={this.togglePersonsHandler}>Click to hide/show</button>
</div>
)
}
}
export default App;
and it is working fine.
My question is:
when this works - without making copy in this case on persons
deletePersonHandler = index => {
const persons = this.state.persons;
persons.splice(index,1);
this.setState({persons:persons})
}
why the recommended way is to make the COPY FIRST AND THEN MODIFY THE REFERENCE TYPE ?
deletePersonHandler = index => {
const persons = [...];
persons.splice(index,1);
this.setState({persons:persons})
}
I am explaining this based on my experience with object in JavaScript. I had one publisher and subscriber code where there was an array of object which used to keep track of some message number and their handler like this
let observer = {"8000":[handler1, handler2]};
So when something happens i publish 8000 message and all handlers get executed like this
for(var item in observer[8000]){
//execute handler/
}
. Till here it was working pretty cool. Then I started removing handler when it has been processed. So after removing handler length of array observer[8000] reduced by 1. So in next sequence it could not find next handler which didn't execute(Objects are pass by reference in JavaScript). So to resolve this I had to make a copy of array object before directly modifying this. In short if object has many dependencies then before processing make copy of it then process or if it is used only single place then use in place processing. It depends on situation, there aren't any strict rules to follow like copy then process.
As you can see in below screencast, manipulating the second input field in my form also changes the value of the first one. I've completely reproduced it on JSfiddle in an isolated environment to track down the bug but didn't find it.
Quick overview of my code base:
A functional component ModuleBuilder has an Array in its state called blocks and a renderFunction which renders for each element of blocks one instance of a subcomponent called ModuleElement.
The subcomponent ModuleElement displays an input form element. When I create n instances of the subcomponent and try to type something in the the form element of the second instance then the first one's value gets updated as well.
const ModuleElement = (props) => {
const blockElement = props.config;
const key = props.index;
const changeValue = (e) => {
blockElement[e.target.name] = e.target.value;
props.updateBlock(key, blockElement);
};
return (
<div key={`block_${key}`}>
<input
key={`block_input_${key}`}
type="text"
id={`fieldname_${key}`}
name="fieldName"
onChange={changeValue}
placeholder="Field Name"
defaultValue={blockElement.fieldName}
/>
</div>
);
};
const ModuleBuilder = () => {
const emptyBlock = {
fieldName: "",
required: true,
};
const [blocks, setBlocks] = React.useState([emptyBlock]);
const updateBlock = (key, data) => {
//console.log(`i was asked to update element #${key}`);
//console.log("this is data", data);
let copyOfBlocks = blocks;
//copyOfBlocks[key] = data; // WHY DOES COMMENTING THIS OUT STILL MAKE THE UPDATE WORK??
setBlocks([...copyOfBlocks]);
// console.log("this is blocks", blocks);
};
const add1Block = () => {
setBlocks([...blocks, emptyBlock]);
};
const renderBlockFormElements = () => {
return blocks.map((value, index) => {
return (
<li key={index}>
<b>Subcomponent with Index #{index}</b>
<ModuleElement
index={index}
config={value}
updateBlock={updateBlock}
/>
</li>
);
});
};
return (
<div>
<h1>
Click twice on "add another field" then enter something into the second
field.
</h1>
<h2>Why is 1st field affected??</h2>
<form>
<ul className="list-group">{renderBlockFormElements()}</ul>
<button type="button" onClick={add1Block}>
Add another field
</button>
</form>
<br></br>
<h2>This is the state:</h2>
{blocks.map((value, index) => (
<p key={`checkup_${index}`}>
<span>{index}: </span>
<span>{JSON.stringify(value)}</span>
</p>
))}
</div>
);
};
ReactDOM.render(<ModuleBuilder />, document.querySelector("#app"));
see full code on https://jsfiddle.net/g8yc39Lv/3/
UPDATE 1:
I solved the problem (see my own answer below) but am still confused about one thing: Why is copyOfBlocks[key] = data; in the updateBlocks() not necessary to update the state correctly? Could it be that the following code is manipulating the props directly??
const changeValue = (e) => {
blockElement[e.target.name] = e.target.value;
props.updateBlock(key, blockElement);
};
If yes, what would be the react way to structure my use case?
UPDATE 2:
It turns out indeed that I was manipulating the props directly. I changed now the whole setup as follows.
The Module Element component now has its own state. OnChange the state is updated and the new target.value is pushed to the props.updateblocks method. See updated JSfiddle here. Is this the react way to do it ?
Suggested changes to your new answer:
Your updated answer is mostly correct, and more React-ish. I would change a couple things:
You have two state variables, which means you have two sources of truth. It works now because they're always in sync, but this has the potential to cause future bugs, and is not actually necessary. ModuleElement doesn't need a state variable at all; you can just render props.config.fieldName:
<input
...
onChange={(e) => {
props.updateBlock(key, {fieldName:e.target.value, required:true})
}}
value={props.config.fieldName}
/>
Then, you can eliminate the state variable in ModuleElement:
const ModuleElement = (props) => {
const key = props.index;
return (
<React.Fragment>
...
I would write updateBlock a little differently. copyOfBlocks is not actually a copy, so copyOfBlocks[key] = data; actually mutates the original data, and a copy is not made until setBlocks([...copyOfBlocks]);. This isn't a problem per se, but a clearer way to write it could be:
const updateBlock = (key, data) => {
let copyOfBlocks = [...blocks];
copyOfBlocks[key] = data;
setBlocks(copyOfBlocks);
};
Now, copyOfBlocks is actually a shallow copy ([...blocks] is what causes the copy), and not a pointer to the same data.
Here's an updated fiddle.
Answers to your remaining questions:
//copyOfBlocks[key] = data; // WHY DOES COMMENTING THIS OUT STILL MAKE THE UPDATE WORK??
Because this line doesn't actually copy:
let copyOfBlocks = blocks;
// This outputs `true`!
console.log(copyOfBlocks === blocks);
This means that, when you do this in ModuleElement - changeValue...
blockElement[e.target.name] = e.target.value;
... you're mutating the same value as when you do this in ModuleBuilder - updateBlock ...
copyOfBlocks[key] = data;
... because copyOfBlocks[key] === blocks[key], and blocks[key] === ModuleBuilder's blockElement. Since both updaters had a reference to the same object, you were updating the same object twice.
But beyond that, mutation of state variables in React is an anti-pattern. This is because React uses === to detect changes. In this case, React cannot tell that myState has changed, and so will not re-render the component:
const [myState, setMyState] = useState({'existing key': 'existing value'});
// ...
// This is BAD:
myState['key'] = ['value'];
const [myState, setMyState] = useState({'existing key': 'existing value'});
// ...
// This is still BAD, because the state isn't being copied:
const newState = myState;
newState['key'] = ['value'];
// At this point, myState === newState
// React will not recognize this change!
setMyState(newState);
Instead, you should write code that
performs a shallow copy on myState, so the old state !== the new state, and
uses setMyState() to tell React that the state has changed:
const [myState, setMyState] = useState({'existing key': 'existing value'});
// ...
// This is correct, and uses ES6 spread syntax to perform a shallow copy of `myState`:
const newState = {...myState, key: 'value'};
// Now, newState !== myState. This is good :)
setMyState(newState);
Or, as a one-liner:
setMyState({...myState, key: 'value'});
The Module Element component now has its own state. onChange the state is updated and the new target.value is pushed to the props.updateblocks method. Is this the react way to do it?
For the most part, yes. You're duplicating your state in two variables, which is unnecessary and more likely to cause bugs in the future. See above for a suggestion on how to eliminate the additional state variable.
I was able to solve the problem after coincidentally reading this question.
I replaced the setBlocks line here:
const emptyBlock = {
fieldName: "",
dataType: "string",
required: true,
};
const add1Block = () => {
setBlocks([
...blocks,
emptyBlock
]);
};
by this statement
const add1Block = () => {
setBlocks([
...blocks,
{ fieldName: "", dataType: "string", required: true },
]);
};
and it turned out that by placing emptyBlock as a default value for a new element I was just apparently just re-referencing it.
Inside this codepen there is React application that renders list of ToDo's. It uses .map() index parameter as key values to render this list. And yes - I know that this is the source of this behaviour, so Please don't tell this in answers.
I've added few items there after initial load and then I see this:
Then I sort it by date and type something into first item
Then I click on Sort by Latest and get this:
Question: Why React fails to render changes in array of JSX element when using .map index as a key in this particular example?
P.S. Here is the code for convenience:
const ToDo = props => (
<tr>
<td>
<label>{props.id}</label>
</td>
<td>
<input />
</td>
<td>
<label>{props.createdAt.toTimeString()}</label>
</td>
</tr>
);
class ToDoList extends React.Component {
constructor() {
super();
const date = new Date();
const todoCounter = 1;
this.state = {
todoCounter: todoCounter,
list: [
{
id: todoCounter,
createdAt: date,
},
],
};
}
sortByEarliest() {
const sortedList = this.state.list.sort((a, b) => {
return a.createdAt - b.createdAt;
});
this.setState({
list: [...sortedList],
});
}
sortByLatest() {
const sortedList = this.state.list.sort((a, b) => {
return b.createdAt - a.createdAt;
});
this.setState({
list: [...sortedList],
});
}
addToEnd() {
const date = new Date();
const nextId = this.state.todoCounter + 1;
const newList = [
...this.state.list,
{id: nextId, createdAt: date},
];
this.setState({
list: newList,
todoCounter: nextId,
});
}
addToStart() {
const date = new Date();
const nextId = this.state.todoCounter + 1;
const newList = [
{id: nextId, createdAt: date},
...this.state.list,
];
this.setState({
list: newList,
todoCounter: nextId,
});
}
render() {
return (
<div>
<code>key=index</code>
<br />
<button onClick={this.addToStart.bind(this)}>
Add New to Start
</button>
<button onClick={this.addToEnd.bind(this)}>
Add New to End
</button>
<button onClick={this.sortByEarliest.bind(this)}>
Sort by Earliest
</button>
<button onClick={this.sortByLatest.bind(this)}>
Sort by Latest
</button>
<table>
<tr>
<th>ID</th>
<th />
<th>created at</th>
</tr>
{this.state.list.map((todo, index) => (
<ToDo key={index} {...todo} />
))}
</table>
</div>
);
}
}
ReactDOM.render(
<ToDoList />,
document.getElementById('root')
);
It seems that inputs are not changed at all here and as they are not controlled (so their attributes/text values didn't change), it makes sense as the root element is not changed in context of React reconciliation (it has still same key). Then React goes to text nodes that eventually are different and then refreshes (updates DOM) only those changed nodes.
So there are two variants to make this work:
Use meaningful key, not index from .map()
Add (in our specific case) attributes to <input />
In general first approach is better as the key property will invalidate the root element (in our case whole ToDo) and force it to render itself, saving us comparison of nested subtree.
Question: Why React fails to render changes in array of JSX element when using .map index as a key in this particular example?
Don't use the list index as a key. The point of the key is to identify your individual entries from one render to another. When you change the order of the array, react will not know that anything has changed, because the order of the indexes in the todo array is still 0, 1, 2, 3 ... Instead use the todo.id as key.
React hasn't failed to render the new order. It's doing exactly as expected. You are telling it the order is exactly the same as last render, but that the properties of the individual todo items have changed.
What you want to say is that it's the ordering that has changed. Then you have to pass a meaningful key. Then react will use the same dom elements, but change the order.
This means that you can do something like react-shuffle where a transition effect lets you see how the elements are reordered.
This is because you do not have stable ID's (your indexes change after the filter). See the docs here.
And you have another problem, because you are mutating your list when you sort it:
sortByEarliest() {
const sortedList = this.state.list.sort((a, b) => {
return a.createdAt - b.createdAt;
});
this.setState({
list: [...sortedList],
});
}
sortByLatest() {
const sortedList = this.state.list.sort((a, b) => {
return b.createdAt - a.createdAt;
});
this.setState({
list: [...sortedList],
});
}
The sort alter your array. So you could use this solution:
sortByEarliest() {
const { list } = this.state;
list.sort((a, b) => a.createdAt - b.createdAt);
this.setState({ list });
}
sortByLatest() {
const { list } = this.state;
list.sort((a, b) => b.createdAt - a.createdAt);
this.setState({ list });
}
I am trying to remove an element from an array when the onClick on the remove button is called, when the button is clicked I am getting Uncaught TypeError: Cannot read property 'name' of null
Why am I getting this error?
removeData(key) {
console.log(key);
const data = this.state.data;
data[key] = null;
this.setState({ data });
}
renderData(key){
const user = this.props.data[key];
console.log(user.name);
console.log(user.id);
return(
<div key={key}>
<li> <strong> Name: </strong> {user.name},
<strong> ID: </strong> {user.id} </li>
<button onClick={() => this.props.removeData(key)}> Remove </button>
</div>
)
}
Why am I getting this error?
You are explicitly setting data[key] to null:
data[key] = null;
When the component rerenders it presumably calls renderData with the key that was "removed" (because the property is still there, its value is just null). user will be null and accessing null.name throws an error.
Depending on what you actually want, either skip null values in your renderData method, or actually delete the entry with
delete data[key];
Assigning null does not delete a property:
var obj = {foo: 42};
// Doesn't actually remove the property
obj.foo = null;
console.log(obj);
// This removes the property
delete obj.foo;
console.log(obj);
I am trying to remove an element from an array
If you really have an array, then there more things wrong. {...this.state.data} is not an appropriate way to clone an array because you end up with an object.
To properly perform this action on an array you would do
removeData(key) {
// Assuming `key` is the index of the element
// Create copy
const data = Array.from(this.state.data);
// Remove element at index `key`
data.splice(key, 1);
this.setState({ data });
}
Try:
renderData(key){
const user = this.props.data[key] || {}; // change
console.log(user.name);
console.log(user.id);
return(
<div key={key}>
<li> <strong> Name: </strong> {user.name},
<strong> ID: </strong> {user.id} </li>
<button onClick={() => this.props.removeData(key)}> Remove </button>
</div>
)
It seems like your data isn't ready when the component renders, so putting in a blank object will allow the component to render empty while the data loads.
edit: You could consider:
renderData(key){
if (!this.props.data[key]) return null; //change
const user = this.props.data[key];
console.log(user.name);
console.log(user.id);
return(
<div key={key}>
<li> <strong> Name: </strong> {user.name},
<strong> ID: </strong> {user.id} </li>
<button onClick={() => this.props.removeData(key)}> Remove </button>
</div>
)
This would hide the component as the data is being fetched. It's up to you then to communicate that the data is loading to the user. You could pass down a loading prop that is set to true when the request is fired and set to false when the request returns data and show either some loading text or an animation.
Removing objects from arrays
A good way to remove objects from arrays is to use filter. Using filter keeps us safe from bugs that come from using the index inside of the map function. data.map((item,index) => <div key={index} onClick={this.removeData(index)}> {item.fruit} </div>). This can cause serious problems when removing objects from the array.
What react says about this.
We don’t recommend using indexes for keys if the order of items may
change. This can negatively impact performance and may cause issues
with component state. Check out Robin Pokorny’s article for an
in-depth explanation on the negative impacts of using an index as a
key. If you choose not to assign an explicit key to list items then
React will default to using indexes as keys.
The Filter Approach
This approach takes whatever id the object has and returns every object that has a different id.
this.setState({ data: temp.filter(item => item.id !== id) });
Working Example
import React, { Component } from 'react';
import { render } from 'react-dom';
class App extends Component {
constructor() {
super();
this.state = {
data:[
{ fruit: "apple", id:1 },
{ fruit: "orange", id:2 },
{ fruit: "pineapple", id:3 },
],
}
}
removeData = (id) => {
const { data } = this.state;
const temp = data.slice();
this.setState({ data: temp.filter(item => item.id !== id) });
}
renderData = () =>{
const { data } = this.state;
return data.map((item) => {
return <div key={item.id}>
<label> {item.fruit} </label>
{item.id}
<button onClick={() => this.removeData(item.id)}> Remove </button>
</div>
})
}
render() {
return (
<div>
{this.renderData()}
</div>
);
}
}
render(<App />, document.getElementById('root'));