【问题标题】:React: Re-render parent component based on Child componentReact:基于子组件重新渲染父组件
【发布时间】:2018-10-03 15:35:47
【问题描述】:

我有一个名为Cart 的父组件和另一个名为Items 的组件。 基本上,我从 Node.js 获取一组对象并将其保存为Cart 组件中的状态。

我的购物车组件:

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;

项目组件

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;

逻辑:我的Cart 组件将从 Node.js 的端点获取数据。它是一个对象数组,Cart 组件使用它来填充item 组件。 item 组件有一个删除按钮,一旦触发器将发送它想要删除的项目的名称并使用 MongoDB 的查询来删除这些项目。 但是后台显示删除成功了,但是父组件Cart还是渲染了被删除的item。我想找到一种方法来在项目被删除后触发重新渲染购物车。

编辑:

来自 url 的简单数据是一个对象数组,如下所示:

[{ desc: 'Market', total: 4000, amount: 1 }
{ desc: 'Data', total: 17000, amount: 1 }]

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

response.data,我得到express: Array(1),所以从response.data.express,我实际上得到了数据数组

在@Code-Apprentice 的编辑之后。现在的问题是,在第一个 remove 调用完成后,调用的第二个 axios 没有调用 get invoke。我试图打印出我从第二个 axios 返回的数据,它与第一次渲染的数据相同。

【问题讨论】:

  • 我建议将逻辑从 Item 移出并移到父组件中 - 您可以使用回调道具让父组件知道何时单击删除。然后父组件可以更新自己的状态(这将触发重新渲染)。
  • @JoeClay 我是 React 新手,如何将删除逻辑移动到父组件
  • 请显示来自componentDidMount 中初始请求的this.state.cartData 示例。那么handleDelete() 中的.then() 处理程序中的response 是什么。在向/myCart 发出请求之前,您可能需要在调用/remove 路由之后使用.then

标签: javascript reactjs


【解决方案1】:

要触发任何组件的重新渲染,您可以调用this.setState()。在这种情况下,您需要从cartData 中删除已删除的项目并调用类似this.setState({cartData: updatedData}); 的名称。 (确保映射到cartData 而不是render() 中的testData。)

为了做到这一点,您需要一个回调函数,该函数作为道具从Cart 传递到Item。例如,称之为onDelete()。该函数负责实现上一段中描述的逻辑。您可以通过以下两种方式之一来实现:

  1. 从后端获取数据。这将为您提供数据库中项目的准确列表。

  2. this.state.cardData 中删除已删除的项目。这更快,因为您不必发出网络请求,但它有删除错误项目的风险,因此组件的状态与后端数据库中的数据不同。

附录:

在您当前的版本中,您尝试使用这行代码传递回调:

delete={this.handleDelete(item.desc)}

问题在于,当您映射购物车数据中的每个商品时,它会立即调用this.handleDelete()。因此,每个项目都被删除。相反,您需要创建一个在单击按钮时执行此操作的函数:

delete={() => this.handleDelete(item.desc)}

【讨论】:

  • 我试过这种方式。但是,每当我尝试删除一个时,它们都会被删除。此外,即使我从新获取的数据中设置状态,也没有 setstate 触发器。请查看我的编辑。
  • 其实我也不知道为什么,只要页面被渲染,就会调用delete动作。并且所有的项目都被删除了
  • @SpencerOvetsky 查看我的编辑以向您展示如何修复您的代码。
  • 谢谢,我采用了另一种方法,即在客户端而不是服务器中操作数据,然后发送到服务器,它似乎有效。谢谢!
  • @SpencerOvetsky 听起来很棒!对于这两种方法,同样的概念适用,delete 属性必须设置为一个函数,而不是函数调用的返回值(当然,除非函数调用返回一个函数)
【解决方案2】:

所以基本上一旦你删除了一个项目,你就需要在你的购物车组件中更新你的状态。为此,您可以再次获取数据或从购物车状态中删除该商品。在接下来的 sn-p 中,handleDelete 会拼接 cartData 中的 item,并将其设置为触发组件渲染的 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)})
}

【讨论】:

  • 所以 handleDelete 只是再次获取状态?
【解决方案3】:

您应该在父组件中创建一个带有项目 id 的函数,该函数将负责发出删除请求,并在响应返回后从数组中删除已删除的项目并为父组件设置状态。 将此函数发送到父渲染方法上的每个项目。 在子组件中,当用户点击删除按钮时,会从父组件调用传递的函数,并传递函数的id。

【讨论】:

    猜你喜欢
    • 2019-02-26
    • 2019-01-31
    • 2018-08-12
    • 2019-06-01
    • 2018-10-07
    • 2018-11-12
    • 2020-05-01
    • 2020-02-13
    • 2020-08-18
    相关资源
    最近更新 更多