简体   繁体   English

反应:基于子组件重新渲染父组件

[英]React: Re-render parent component based on Child component

I have one parent component, named Cart and another component named Items . 我有一个父组件,名为Cart ,另一个组件为Items Basically, I am getting an array of objects from Node.js and save it as a state in Cart component. 基本上,我从Node.js获取对象数组,并将其另存为Cart组件中的状态。

My Cart Component: 我的购物车组件:

class Cart extends React.Component {
    constructor(props){
        super(props)

        this.state={
            cartData:null,
        }

    }

    componentWillUnmount(){
        _isMounted = false

    }

    componentDidMount() {
        _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 component will get data from the endpoint at Node.js. 逻辑:我的Cart组件将从Node.js的端点获取数据。 It is an array of objects and Cart component use it to populate the item component. 它是一个对象数组, Cart组件使用它来填充item组件。 The item component 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. item组件具有一个删除按钮,一旦触发,该按钮将发送要删除的项目的名称,并使用MongoDB的查询删除项目。 However, the backend showed that it is successfully deleted it, but the parent Cart component still render's the items that have been deleted. 但是,后端显示已成功删除它,但是父级Cart组件仍呈现已删除的项目。 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: 来自url的简单数据是一个像这样的对象数组:

[{ 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: handleDelete()中的响应是被称为axios的响应,其格式如下:

{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 response.data ,我得到express: Array(1)所以从response.data.express ,我实际上是在获取数据数组

After @Code-Apprentice's edit. 在@ Code-Apprentice编辑之后。 Now the problem is that, the second axios called did not invoke get invoke after the first remove call is done. 现在的问题是,在完成第一个删除调用之后,第二个被调用的axios没有调用get invoke。 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. 我试图打印出我从第二个轴测仪中得到的结果,它与第一个渲染的数据相同。

To trigger a rerender of any component, you can call this.setState() . 要触发任何组件的重新渲染,可以调用this.setState() In this case, you need to remove the deleted item from cartData and call something like this.setState({cartData: updatedData}); 在这种情况下,您需要从cartData删除已删除的项目,并调用类似this.setState({cartData: updatedData}); . (Be sure you map over cartData instead of testData in render() .) (确保映射到cartData而不是render()testData 。)

In order to do this, you need a callback function that is passed as a prop from Cart to Item . 为此,您需要一个回调函数,该回调函数作为prop从Cart传递到Item Call it onDelete() , for example. 例如,将其onDelete() This function is responsible to implement the logic described in the previous paragraph. 该功能负责实现上一段中描述的逻辑。 You can implement this in one of two ways: 您可以通过以下两种方式之一来实现此目的:

  1. Fetch the data from the back end. 从后端获取数据。 This will give you exactly the list of items that are in your database. 这将为您提供数据库中项目的准确列表。

  2. Remove the deleted item from this.state.cardData . 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 component'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. 问题在于,当您映射购物车数据中的每个项目时, this.handleDelete() 立即调用this.handleDelete() 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)}

So basically once you delete an item you need update your state in your cart component. 因此,基本上,删除商品后,您需要更新购物车组件中的状态。 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 component render. 在以下代码段中,handleDelete将从cartData中拼接该项并将其设置为state,这将触发组件渲染。

<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)})
}

You should create a function in the parent component 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. 您应该在父组件中创建一个带有项ID的函数,该函数将负责发出删除请求,并在响应返回后,从数组和setState中删除父项的已删除项。 Send this function to each item on parent render method. 将此函数发送到父渲染方法上的每个项目。 In the child component when the user click the delete button will invoke the passed function from the parent and pass the function the id. 在子组件中,当用户单击删除按钮时,将从父组件调用传递的函数,并将ID传递给该函数。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM