I have one parent ponent, named Cart
and another ponent named Items
.
Basically, I am getting an array of objects from Node.js and save it as a state in Cart
ponent.
My Cart Component:
class Cart extends React.Component {
constructor(props){
super(props)
this.state={
cartData:null,
}
}
ponentWillUnmount(){
_isMounted = false
}
ponentDidMount() {
_isMounted = true
this.callApi()
.then(res => {if(_isMounted) this.setState({cartData: res.express})})
.catch(err => console.log(err));
}
callApi = async () => {
const response = await fetch('/myCart');
const body = await response.json();
if (response.status !== 200) throw Error(body.message);
return body;
};
mapOutItems(){
return this.state.cartData.map((item,index) => (
<Item
key={index}
desc={item.desc}
amount={item.amount}
total={item.total}
delete={this.handleDelete(item.desc)}
/>
))
}
handleDelete(desc){
axios({method: "post", url: "/remove", data:{name: desc}})
axios("/myCart").then(function (response){
this.setState({cartData : response.data.express})
})
.catch(function (error){
console.log(error)
});
}
render(){
return(
<div>
<div className="container">
{this.mapOutItems()}
</div>
</div>
)
}
}
export default Cart;
Item Component
class Item extends React.Component{
handleClick(e){
e.preventDefault()
axios({method: "post", url: "/remove", data:{name: this.props.desc}})
}
render(){
return(
<div>
<div className="row">
<div className="col">
{this.props.desc}
</div>
<div className="col">
{this.props.amount}
</div>
<div className="col">
{this.props.total}
</div>
<button className="btn btn-sm btn-danger" onClick=onClick={this.props.delete}>
Delete
</button>
</div>
</div>
)
}
}
export default Item;
Logic: My Cart
ponent will get data from the endpoint at Node.js. It is an array of objects and Cart
ponent use it to populate the item
ponent. The item
ponent has a delete button that once trigger will send the name of the item it wants to delete and use MongoDB's query to delete the items.
However, the backend showed that it is successfully deleted it, but the parent Cart
ponent still render's the items that have been deleted. I wanted to find a way to trigger a re-render a Cart once the item is deleted.
Edit:
The simple data from the url is an array of object like that looks like this:
[{ desc: 'Market', total: 4000, amount: 1 }
{ desc: 'Data', total: 17000, amount: 1 }]
Response in the handleDelete() is the response of axios called, with format like the following:
{data: {…}, status: 200, statusText: "OK", headers: {…}, config: {…}, …}
config: {adapter: ƒ, transformRequest: {…}, transformResponse: {…}, timeout: 0, xsrfCookieName: "XSRF-TOKEN", …}
data: {express: Array(1)}
headers: {date: "Wed, 03 Oct 2018 18:30:02 GMT", etag: "W/"37-QxQ/lxfNH/fWEmtfXYAG1YLiA/E"", x-powered-by: "Express", content-length: "55", vary: "Accept-Encoding", …}
request: XMLHttpRequest {onreadystatechange: ƒ, readyState: 4, timeout: 0, withCredentials: false, upload: XMLHttpRequestUpload, …}
status: 200
statusText: "OK"
__proto__: Object
From response.data
, i get express: Array(1)
so from the response.data.express
, I am actually getting the array of data
After @Code-Apprentice's edit. Now the problem is that, the second axios called did not invoke get invoke after the first remove call is done. i tried to print out what I got back from that that second axios and it is the same data as the what are first rendered.
I have one parent ponent, named Cart
and another ponent named Items
.
Basically, I am getting an array of objects from Node.js and save it as a state in Cart
ponent.
My Cart Component:
class Cart extends React.Component {
constructor(props){
super(props)
this.state={
cartData:null,
}
}
ponentWillUnmount(){
_isMounted = false
}
ponentDidMount() {
_isMounted = true
this.callApi()
.then(res => {if(_isMounted) this.setState({cartData: res.express})})
.catch(err => console.log(err));
}
callApi = async () => {
const response = await fetch('/myCart');
const body = await response.json();
if (response.status !== 200) throw Error(body.message);
return body;
};
mapOutItems(){
return this.state.cartData.map((item,index) => (
<Item
key={index}
desc={item.desc}
amount={item.amount}
total={item.total}
delete={this.handleDelete(item.desc)}
/>
))
}
handleDelete(desc){
axios({method: "post", url: "/remove", data:{name: desc}})
axios("/myCart").then(function (response){
this.setState({cartData : response.data.express})
})
.catch(function (error){
console.log(error)
});
}
render(){
return(
<div>
<div className="container">
{this.mapOutItems()}
</div>
</div>
)
}
}
export default Cart;
Item Component
class Item extends React.Component{
handleClick(e){
e.preventDefault()
axios({method: "post", url: "/remove", data:{name: this.props.desc}})
}
render(){
return(
<div>
<div className="row">
<div className="col">
{this.props.desc}
</div>
<div className="col">
{this.props.amount}
</div>
<div className="col">
{this.props.total}
</div>
<button className="btn btn-sm btn-danger" onClick=onClick={this.props.delete}>
Delete
</button>
</div>
</div>
)
}
}
export default Item;
Logic: My Cart
ponent will get data from the endpoint at Node.js. It is an array of objects and Cart
ponent use it to populate the item
ponent. The item
ponent has a delete button that once trigger will send the name of the item it wants to delete and use MongoDB's query to delete the items.
However, the backend showed that it is successfully deleted it, but the parent Cart
ponent still render's the items that have been deleted. I wanted to find a way to trigger a re-render a Cart once the item is deleted.
Edit:
The simple data from the url is an array of object like that looks like this:
[{ desc: 'Market', total: 4000, amount: 1 }
{ desc: 'Data', total: 17000, amount: 1 }]
Response in the handleDelete() is the response of axios called, with format like the following:
{data: {…}, status: 200, statusText: "OK", headers: {…}, config: {…}, …}
config: {adapter: ƒ, transformRequest: {…}, transformResponse: {…}, timeout: 0, xsrfCookieName: "XSRF-TOKEN", …}
data: {express: Array(1)}
headers: {date: "Wed, 03 Oct 2018 18:30:02 GMT", etag: "W/"37-QxQ/lxfNH/fWEmtfXYAG1YLiA/E"", x-powered-by: "Express", content-length: "55", vary: "Accept-Encoding", …}
request: XMLHttpRequest {onreadystatechange: ƒ, readyState: 4, timeout: 0, withCredentials: false, upload: XMLHttpRequestUpload, …}
status: 200
statusText: "OK"
__proto__: Object
From response.data
, i get express: Array(1)
so from the response.data.express
, I am actually getting the array of data
After @Code-Apprentice's edit. Now the problem is that, the second axios called did not invoke get invoke after the first remove call is done. i tried to print out what I got back from that that second axios and it is the same data as the what are first rendered.
Share Improve this question edited Oct 3, 2018 at 18:33 Spencer Ovetsky asked Oct 3, 2018 at 15:35 Spencer OvetskySpencer Ovetsky 1701 gold badge6 silver badges14 bronze badges 3-
I'd remend moving the logic out of your
Item
and into the parent ponent - you can use a callback prop to let the parent ponent know when delete has been clicked. Then the parent ponent can update its own state (which will trigger a re-render). – Joe Clay Commented Oct 3, 2018 at 15:43 - @JoeClay I am new to React, how would I move the delete logic to the parent ponent – Spencer Ovetsky Commented Oct 3, 2018 at 15:49
-
Please show an example of
this.state.cartData
that es from the initial request inponentDidMount
. Then what isresponse
in the.then()
handler inhandleDelete()
. You probably need a.then
after the call to the/remove
route before you make a request to/myCart
. – Code-Apprentice Commented Oct 3, 2018 at 18:23
3 Answers
Reset to default 2To trigger a rerender of any ponent, you can call this.setState()
. In this case, you need to remove the deleted item from cartData
and call something like this.setState({cartData: updatedData});
. (Be sure you map over cartData
instead of testData
in render()
.)
In order to do this, you need a callback function that is passed as a prop from Cart
to Item
. Call it onDelete()
, for example. This function is responsible to implement the logic described in the previous paragraph. You can implement this in one of two ways:
Fetch the data from the back end. This will give you exactly the list of items that are in your database.
Remove the deleted item from
this.state.cardData
. This is quicker because you don't have to make a network request, but it risks deleting the wrong item so that the ponent's state is not the same as the data in the backend database.
Addendum:
In your current version, you attempt to pass a callback with this line of code:
delete={this.handleDelete(item.desc)}
The problem is that this calls this.handleDelete()
immediately as you map over each item in your cart data. So every item is deleted. Instead, you need to create a function which will do this when the button is clicked:
delete={() => this.handleDelete(item.desc)}
You should create a function in the parent ponent that take an item id and this function will be responsible for making the delete request and after the response return remove the deleted item from the array and setState for the parent. Send this function to each item on parent render method. In the child ponent when the user click the delete button will invoke the passed function from the parent and pass the function the id.
So basically once you delete an item you need update your state in your cart ponent. To do that you can again fetch the data or delete just that item from cart state. In the following snippet, handleDelete will splice the item from cartData and set it to state which will trigger ponent render.
<Item
key={index}
desc={item.desc}
amount={item.amount}
total={item.total}
onDelete={this.handleDelete}
/>
handleClick(e){
e.preventDefault()
axios({method: "post", url: "/remove", data:{name: this.props.desc}}).then(() => {this.props.handleDelete(this.props.desc)})
}
发布者:admin,转转请注明出处:http://www.yc00.com/questions/1745232788a4617753.html
评论列表(0条)